hypertune
Version:
[Hypertune](https://www.hypertune.com/) is the most flexible platform for feature flags, A/B testing, analytics and app configuration. Built with full end-to-end type-safety, Git-style version control and local, synchronous, in-memory flag evaluation. Opt
170 lines • 8.52 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const shared_1 = require("../shared");
const RemoteLogger_1 = __importDefault(require("../shared/helpers/RemoteLogger"));
const LRUCache_1 = __importDefault(require("../shared/helpers/LRUCache"));
const getNodeCacheKey_1 = __importDefault(require("./getNodeCacheKey"));
const newTracedFetch_1 = __importDefault(require("../shared/helpers/newTracedFetch"));
const fetchMaxKeepAliveRequestSizeBytes = 64000;
/**
* Logger provides a high level API for Node and generic SDK logs. It emits
* these logs using the `localLogger` and it also forwards them to the
* `remoteLogger` based on the provided `remoteLoggingMode`.
*/
class Logger {
constructor({ id, traceId, token, remoteLoggingMode, remoteFlushIntervalMs, remoteLoggingEndpointUrl, localLogger, logsHandler, }) {
this.remoteLogCache = new LRUCache_1.default(10000);
this.id = id;
this.remoteLoggingMode = remoteLoggingMode;
this.logsHandler = logsHandler;
this.localLogger = localLogger;
this.remoteLogger =
remoteLoggingMode === "off"
? null
: new RemoteLogger_1.default({
traceId,
token,
createLogs: getCreateLogsFunction((0, newTracedFetch_1.default)({
timeoutMs: 20000,
localLogger: this.localLogger,
}), remoteLoggingEndpointUrl),
localLogger: this.localLogger,
flushIntervalMs: remoteFlushIntervalMs,
});
if (remoteLoggingMode === "off") {
this.localLogger(shared_1.LogLevel.Info, "Remote logging is disabled.", {
traceId,
});
}
}
nodeLog({ commitId, initDataHash, nodeTypeName, nodePath, nodeExpression, reductionLogs, }) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const baseLogMetadata = {
sdkVersion: shared_1.sdkVersion,
nodeTypeName,
nodePath,
nodeExpression,
reductionLogs,
};
this.logsHandler({
messageList: (_a = reductionLogs.messageList) !== null && _a !== void 0 ? _a : [],
eventList: (_b = reductionLogs.eventList) !== null && _b !== void 0 ? _b : [],
exposureList: (_c = reductionLogs.exposureList) !== null && _c !== void 0 ? _c : [],
evaluationList: (_d = reductionLogs.evaluationList) !== null && _d !== void 0 ? _d : [],
});
(_e = reductionLogs.messageList) === null || _e === void 0 ? void 0 : _e.forEach(({ level, message, metadata }) => {
var _a;
const logMessage = `${nodeTypeName}Node at ${nodePath}: ${message}`;
const logMetadata = Object.assign(Object.assign({}, baseLogMetadata), metadata);
if ((level === shared_1.LogLevel.Warn || level === shared_1.LogLevel.Error) &&
this.shouldRemoteNodeLog(initDataHash, nodePath, message)) {
(_a = this.remoteLogger) === null || _a === void 0 ? void 0 : _a.log(shared_1.LogType.SdkNode, level, commitId, logMessage, logMetadata);
}
});
if (reductionLogs &&
(reductionLogs.evaluations ||
reductionLogs.eventList ||
reductionLogs.exposureList)) {
if (!commitId) {
const errorMessage = `${nodeTypeName}Node at ${nodePath}: Missing commitId so cannot remote log evaluations, events and exposures.`;
this.localLogger(shared_1.LogLevel.Error, errorMessage, baseLogMetadata);
if (this.shouldRemoteNodeLog(initDataHash, nodePath, errorMessage)) {
(_f = this.remoteLogger) === null || _f === void 0 ? void 0 : _f.log(shared_1.LogType.SdkNode, shared_1.LogLevel.Error, commitId, errorMessage, baseLogMetadata);
}
return;
}
if (reductionLogs.evaluations &&
this.shouldRemoteNodeLog(initDataHash, nodePath, "evaluations")) {
(_g = this.remoteLogger) === null || _g === void 0 ? void 0 : _g.evaluations(commitId, reductionLogs.evaluations);
}
if (reductionLogs.eventList) {
// We always log events to the backend
(_h = this.remoteLogger) === null || _h === void 0 ? void 0 : _h.events(commitId, reductionLogs.eventList);
}
if (reductionLogs.exposureList &&
this.shouldRemoteNodeLog(initDataHash, nodePath, "exposures")) {
(_j = this.remoteLogger) === null || _j === void 0 ? void 0 : _j.exposures(commitId, reductionLogs.exposureList);
}
}
}
shouldRemoteNodeLog(initDataHash, nodePath, cacheKeySuffix) {
switch (this.remoteLoggingMode) {
case "session": {
const cacheKey = (0, getNodeCacheKey_1.default)(initDataHash !== null && initDataHash !== void 0 ? initDataHash : "", nodePath, cacheKeySuffix);
if (this.remoteLogCache.get(cacheKey)) {
this.localLogger(shared_1.LogLevel.Debug, `Remote log cache hit.`,
/* metadata */ { initDataHash, nodePath, cacheKeySuffix });
return false;
}
this.remoteLogCache.set(cacheKey, true);
return true;
}
case "normal": {
return true;
}
case "off": {
return false;
}
default: {
const neverLoggingMode = this.remoteLoggingMode;
throw new Error(`Unexpected logging mode: ${neverLoggingMode}`);
}
}
}
log(level, commitId, message, metadata) {
var _a;
this.localLogger(level, message, metadata);
if (this.remoteLoggingMode !== "off" &&
(level === shared_1.LogLevel.Warn || level === shared_1.LogLevel.Error)) {
(_a = this.remoteLogger) === null || _a === void 0 ? void 0 : _a.log(shared_1.LogType.SdkMessage, level, commitId, message, Object.assign(Object.assign({}, metadata), { sdkVersion: shared_1.sdkVersion }));
}
}
flush(traceId) {
return this.remoteLogger
? this.remoteLogger.flush(traceId)
: Promise.resolve();
}
close(traceId) {
return this.remoteLogger
? this.remoteLogger.close(traceId)
: Promise.resolve();
}
}
exports.default = Logger;
function getCreateLogsFunction(tracedFetch, logsUrl) {
return (traceId, input) => __awaiter(this, void 0, void 0, function* () {
const bodyJson = JSON.stringify(input);
const bodyBlob = new Blob([bodyJson]);
const response = yield tracedFetch(traceId, logsUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
},
body: bodyJson,
// Only use keepalive if the request is smaller than the maximum
// allowed size with keepalive enabled.
keepalive: bodyBlob.size < fetchMaxKeepAliveRequestSizeBytes,
});
if (!response.ok) {
throw new Error(`Failed to create logs status: "${response.status}" response: "${yield response.text()}"`);
}
const data = yield response.json();
if (!data.success) {
throw new Error(`Failed to create logs status: "${response.status}" response: "${JSON.stringify(data)}"`);
}
});
}
//# sourceMappingURL=Logger.js.map