Move logger.ts into logging/index.ts

Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
Christina Ying Wang 2024-11-18 12:15:37 -08:00
parent e62e245fc7
commit c610710f03
25 changed files with 249 additions and 254 deletions

View File

@ -20,7 +20,7 @@ import log from '../lib/supervisor-console';
import * as deviceState from '../device-state';
import * as globalEventBus from '../event-bus';
import * as TargetState from './poll';
import * as logger from '../logger';
import * as logger from '../logging';
import * as apiHelper from '../lib/api-helper';
import { startReporting, stateReportErrors } from './report';

View File

@ -4,7 +4,7 @@ import type StrictEventEmitter from 'strict-event-emitter-types';
import * as config from '../config';
import type { Transaction } from '../db';
import * as logger from '../logger';
import * as logger from '../logging';
import LocalModeManager from '../local-mode';
import * as dbFormat from '../device-state/db-format';

View File

@ -15,7 +15,7 @@ import {
StatusError,
} from '../lib/errors';
import * as LogTypes from '../lib/log-types';
import * as logger from '../logger';
import * as logger from '../logging';
import { ImageDownloadBackoffError } from './errors';
import type { Service } from './service';

View File

@ -6,7 +6,7 @@ import { isNotFoundError } from '../lib/errors';
import logTypes = require('../lib/log-types');
import log from '../lib/supervisor-console';
import * as logger from '../logger';
import * as logger from '../logging';
import { Network } from './network';
import { ResourceRecreationAttemptError } from './errors';

View File

@ -3,7 +3,7 @@ import type dockerode from 'dockerode';
import { docker } from '../lib/docker-utils';
import logTypes = require('../lib/log-types');
import * as logger from '../logger';
import * as logger from '../logging';
import log from '../lib/supervisor-console';
import * as ComposeUtils from './utils';

View File

@ -8,7 +8,7 @@ import type StrictEventEmitter from 'strict-event-emitter-types';
import * as config from '../config';
import { docker } from '../lib/docker-utils';
import * as logger from '../logger';
import * as logger from '../logging';
import { PermissiveNumber } from '../config/types';
import * as constants from '../lib/constants';

View File

@ -8,7 +8,7 @@ import { pathOnData } from '../lib/host-utils';
import { docker } from '../lib/docker-utils';
import * as LogTypes from '../lib/log-types';
import log from '../lib/supervisor-console';
import * as logger from '../logger';
import * as logger from '../logging';
import { ResourceRecreationAttemptError } from './errors';
import type { VolumeConfig } from './types';
import { Volume } from './volume';

View File

@ -7,7 +7,7 @@ import { docker } from '../lib/docker-utils';
import { InternalInconsistencyError } from '../lib/errors';
import * as LogTypes from '../lib/log-types';
import type { LabelObject } from '../types';
import * as logger from '../logger';
import * as logger from '../logging';
import * as ComposeUtils from './utils';
import type {

View File

@ -7,7 +7,7 @@ import { ConfigBackend } from './backend';
import { exec, exists } from '../../lib/fs-utils';
import * as hostUtils from '../../lib/host-utils';
import * as constants from '../../lib/constants';
import * as logger from '../../logger';
import * as logger from '../../logging';
import log from '../../lib/supervisor-console';
/**

View File

@ -4,7 +4,7 @@ import { getGlobalApiKey, refreshKey } from '../lib/api-keys';
import * as messages from './messages';
import * as eventTracker from '../event-tracker';
import * as deviceState from '../device-state';
import * as logger from '../logger';
import * as logger from '../logging';
import * as config from '../config';
import * as hostConfig from '../host-config';
import type {

View File

@ -12,7 +12,7 @@ import * as commitStore from '../compose/commit';
import * as config from '../config';
import * as db from '../db';
import * as deviceConfig from '../device-config';
import * as logger from '../logger';
import * as logger from '../logging';
import * as images from '../compose/images';
import * as volumeManager from '../compose/volume-manager';
import * as serviceManager from '../compose/service-manager';

View File

@ -4,7 +4,7 @@ import { promises as fs } from 'fs';
import * as config from './config';
import * as db from './db';
import * as logger from './logger';
import * as logger from './logging';
import * as dbus from './lib/dbus';
import type { EnvVarObject } from './types';
import { UnitNotLoadedError } from './lib/errors';

View File

@ -6,7 +6,7 @@ import type StrictEventEmitter from 'strict-event-emitter-types';
import prettyMs from 'pretty-ms';
import * as config from '../config';
import * as logger from '../logger';
import * as logger from '../logging';
import * as network from '../network';
import * as deviceConfig from '../device-config';

View File

@ -4,7 +4,7 @@ import * as config from '../config/index';
import * as constants from './constants';
import * as iptables from './iptables';
import { log } from './supervisor-console';
import { logSystemMessage } from '../logger';
import { logSystemMessage } from '../logging';
import * as dbFormat from '../device-state/db-format';

View File

@ -14,7 +14,7 @@ import { docker } from './docker-utils';
import { log } from './supervisor-console';
import { pathOnData } from './host-utils';
import type { Volume } from '../compose/volume';
import * as logger from '../logger';
import * as logger from '../logging';
import type {
DatabaseApp,
DatabaseService,

View File

@ -9,7 +9,7 @@ import { mkdirp } from './fs-utils';
import * as lockfile from './lockfile';
import { takeGlobalLockRW } from './process-lock';
import * as logTypes from './log-types';
import * as logger from '../logger';
import * as logger from '../logging';
export const LOCKFILE_UID = 65534;
export const BASE_LOCK_DIR = '/tmp/balena-supervisor/services';

View File

@ -6,7 +6,7 @@ import * as constants from './lib/constants';
import { docker } from './lib/docker-utils';
import { SupervisorContainerNotFoundError } from './lib/errors';
import log from './lib/supervisor-console';
import * as logger from './logger';
import * as logger from './logging';
// EngineSnapshot represents a list of containers, images, volumens, and networks present on the engine.
// A snapshot is taken before entering local mode in order to perform cleanup when we exit local mode.

View File

@ -1,229 +0,0 @@
import Bluebird from 'bluebird';
import _ from 'lodash';
import * as config from './config';
import * as eventTracker from './event-tracker';
import type { LogType } from './lib/log-types';
import { takeGlobalLockRW } from './lib/process-lock';
import type { LogBackend, LogMessage } from './logging';
import { BalenaLogBackend, LocalLogBackend } from './logging';
import logMonitor from './logging/monitor';
import * as globalEventBus from './event-bus';
import superConsole from './lib/supervisor-console';
type LogEventObject = Dictionary<any> | null;
// export class Logger {
let backend: LogBackend | null = null;
let balenaBackend: BalenaLogBackend | null = null;
let localBackend: LocalLogBackend | null = null;
export const initialized = _.once(async () => {
await config.initialized();
const {
apiEndpoint,
logsEndpoint,
uuid,
deviceApiKey,
unmanaged,
loggingEnabled,
localMode,
} = await config.getMany([
'apiEndpoint',
'logsEndpoint',
'uuid',
'deviceApiKey',
'unmanaged',
'loggingEnabled',
'localMode',
]);
balenaBackend = new BalenaLogBackend(
logsEndpoint ?? apiEndpoint,
uuid,
deviceApiKey,
);
localBackend = new LocalLogBackend();
backend = localMode ? localBackend : balenaBackend;
backend.unmanaged = unmanaged;
backend.publishEnabled = loggingEnabled;
if (!balenaBackend.isInitialised()) {
globalEventBus.getInstance().once('deviceProvisioned', async () => {
const conf = await config.getMany([
'uuid',
'apiEndpoint',
'logsEndpoint',
'deviceApiKey',
]);
// We use Boolean here, as deviceApiKey when unset
// is '' for legacy reasons. Once we're totally
// typescript, we can make it have a default value
// of undefined.
if (_.every(conf, Boolean)) {
// Everything is set, provide the values to the
// balenaBackend, and remove our listener
balenaBackend!.assignFields(
conf.logsEndpoint ?? conf.apiEndpoint,
conf.uuid!,
conf.deviceApiKey,
);
}
});
}
});
export function switchBackend(localMode: boolean) {
if (localMode) {
// Use the local mode backend
backend = localBackend;
superConsole.info('Switching logging backend to LocalLogBackend');
} else {
// Use the balena backend
backend = balenaBackend;
superConsole.info('Switching logging backend to BalenaLogBackend');
}
}
export function getLocalBackend(): LocalLogBackend {
// TODO: Think about this interface a little better, it would be
// nicer to proxy the logs via the logger module
if (localBackend == null) {
// TODO: Type this as an internal inconsistency error
throw new Error('Local backend logger is not defined.');
}
return localBackend;
}
export function enable(value: boolean = true) {
if (backend != null) {
backend.publishEnabled = value;
}
}
export async function log(message: LogMessage) {
await backend?.log(message);
}
export function logSystemMessage(
message: string,
eventObj?: LogEventObject,
eventName?: string,
track: boolean = true,
) {
const msgObj: LogMessage = { message, isSystem: true, timestamp: Date.now() };
if (eventObj != null && eventObj.error != null) {
msgObj.isStdErr = true;
}
// IMPORTANT: this could potentially create a memory leak if logSystemMessage
// is used too quickly but we don't want supervisor logging to hold up other tasks
void log(msgObj);
if (track) {
eventTracker.track(
eventName != null ? eventName : message,
eventObj != null ? eventObj : {},
);
}
}
export function lock(containerId: string): Bluebird.Disposer<() => void> {
return takeGlobalLockRW(containerId).disposer((release) => {
release();
});
}
type ServiceInfo = { serviceId: number };
export async function attach(
containerId: string,
{ serviceId }: ServiceInfo,
): Promise<void> {
// First detect if we already have an attached log stream
// for this container
if (logMonitor.isAttached(containerId)) {
return;
}
return Bluebird.using(lock(containerId), async () => {
await logMonitor.attach(containerId, async (message) => {
await log({ ...message, serviceId });
});
});
}
export function logSystemEvent(
logType: LogType,
obj: LogEventObject,
track: boolean = true,
): void {
let message = logType.humanName;
const objectName = objectNameForLogs(obj);
if (objectName != null) {
message += ` '${objectName}'`;
}
if (obj && obj.error != null) {
let errorMessage = obj.error.message;
if (_.isEmpty(errorMessage)) {
errorMessage =
obj.error.name !== 'Error' ? obj.error.name : 'Unknown cause';
superConsole.warn('Invalid error message', obj.error);
}
message += ` due to '${errorMessage}'`;
}
logSystemMessage(message, obj, logType.eventName, track);
}
export function logConfigChange(
conf: { [configName: string]: string },
{ success = false, err }: { success?: boolean; err?: Error } = {},
) {
const obj: LogEventObject = { conf };
let message: string;
let eventName: string;
if (success) {
message = `Applied configuration change ${JSON.stringify(conf)}`;
eventName = 'Apply config change success';
} else if (err != null) {
message = `Error applying configuration change: ${err}`;
eventName = 'Apply config change error';
obj.error = err;
} else {
message = `Applying configuration change ${JSON.stringify(conf)}`;
eventName = 'Apply config change in progress';
}
logSystemMessage(message, obj, eventName);
}
function objectNameForLogs(eventObj: LogEventObject): string | null {
if (eventObj == null) {
return null;
}
if (
eventObj.service != null &&
eventObj.service.serviceName != null &&
eventObj.service.config != null &&
eventObj.service.config.image != null
) {
return `${eventObj.service.serviceName} ${eventObj.service.config.image}`;
}
if (eventObj.image != null) {
return eventObj.image.name;
}
if (eventObj.network != null && eventObj.network.name != null) {
return eventObj.network.name;
}
if (eventObj.volume != null && eventObj.volume.name != null) {
return eventObj.volume.name;
}
if (eventObj.fields != null) {
return eventObj.fields.join(',');
}
return null;
}

View File

@ -1,5 +1,229 @@
import Bluebird from 'bluebird';
import _ from 'lodash';
import * as config from '../config';
import * as eventTracker from '../event-tracker';
import type { LogType } from '../lib/log-types';
import { takeGlobalLockRW } from '../lib/process-lock';
import { BalenaLogBackend } from './balena-backend';
import { LocalLogBackend } from './local-backend';
import { LogBackend, LogMessage } from './log-backend';
import type { LogBackend, LogMessage } from './log-backend';
import logMonitor from './monitor';
export { LocalLogBackend, LogBackend, LogMessage, BalenaLogBackend };
import * as globalEventBus from '../event-bus';
import superConsole from '../lib/supervisor-console';
type LogEventObject = Dictionary<any> | null;
let backend: LogBackend | null = null;
let balenaBackend: BalenaLogBackend | null = null;
let localBackend: LocalLogBackend | null = null;
export const initialized = _.once(async () => {
await config.initialized();
const {
apiEndpoint,
logsEndpoint,
uuid,
deviceApiKey,
unmanaged,
loggingEnabled,
localMode,
} = await config.getMany([
'apiEndpoint',
'logsEndpoint',
'uuid',
'deviceApiKey',
'unmanaged',
'loggingEnabled',
'localMode',
]);
balenaBackend = new BalenaLogBackend(
logsEndpoint ?? apiEndpoint,
uuid,
deviceApiKey,
);
localBackend = new LocalLogBackend();
backend = localMode ? localBackend : balenaBackend;
backend.unmanaged = unmanaged;
backend.publishEnabled = loggingEnabled;
if (!balenaBackend.isInitialised()) {
globalEventBus.getInstance().once('deviceProvisioned', async () => {
const conf = await config.getMany([
'uuid',
'apiEndpoint',
'logsEndpoint',
'deviceApiKey',
]);
// We use Boolean here, as deviceApiKey when unset
// is '' for legacy reasons. Once we're totally
// typescript, we can make it have a default value
// of undefined.
if (_.every(conf, Boolean)) {
// Everything is set, provide the values to the
// balenaBackend, and remove our listener
balenaBackend!.assignFields(
conf.logsEndpoint ?? conf.apiEndpoint,
conf.uuid!,
conf.deviceApiKey,
);
}
});
}
});
export function switchBackend(localMode: boolean) {
if (localMode) {
// Use the local mode backend
backend = localBackend;
superConsole.info('Switching logging backend to LocalLogBackend');
} else {
// Use the balena backend
backend = balenaBackend;
superConsole.info('Switching logging backend to BalenaLogBackend');
}
}
export function getLocalBackend(): LocalLogBackend {
// TODO: Think about this interface a little better, it would be
// nicer to proxy the logs via the logger module
if (localBackend == null) {
// TODO: Type this as an internal inconsistency error
throw new Error('Local backend logger is not defined.');
}
return localBackend;
}
export function enable(value: boolean = true) {
if (backend != null) {
backend.publishEnabled = value;
}
}
export async function log(message: LogMessage) {
await backend?.log(message);
}
export function logSystemMessage(
message: string,
eventObj?: LogEventObject,
eventName?: string,
track: boolean = true,
) {
const msgObj: LogMessage = { message, isSystem: true, timestamp: Date.now() };
if (eventObj != null && eventObj.error != null) {
msgObj.isStdErr = true;
}
// IMPORTANT: this could potentially create a memory leak if logSystemMessage
// is used too quickly but we don't want supervisor logging to hold up other tasks
void log(msgObj);
if (track) {
eventTracker.track(
eventName != null ? eventName : message,
eventObj != null ? eventObj : {},
);
}
}
function lock(containerId: string): Bluebird.Disposer<() => void> {
return takeGlobalLockRW(containerId).disposer((release) => {
release();
});
}
type ServiceInfo = { serviceId: number };
export async function attach(
containerId: string,
{ serviceId }: ServiceInfo,
): Promise<void> {
// First detect if we already have an attached log stream
// for this container
if (logMonitor.isAttached(containerId)) {
return;
}
return Bluebird.using(lock(containerId), async () => {
await logMonitor.attach(containerId, async (message) => {
await log({ ...message, serviceId });
});
});
}
export function logSystemEvent(
logType: LogType,
obj: LogEventObject,
track: boolean = true,
): void {
let message = logType.humanName;
const objectName = objectNameForLogs(obj);
if (objectName != null) {
message += ` '${objectName}'`;
}
if (obj && obj.error != null) {
let errorMessage = obj.error.message;
if (_.isEmpty(errorMessage)) {
errorMessage =
obj.error.name !== 'Error' ? obj.error.name : 'Unknown cause';
superConsole.warn('Invalid error message', obj.error);
}
message += ` due to '${errorMessage}'`;
}
logSystemMessage(message, obj, logType.eventName, track);
}
export function logConfigChange(
conf: { [configName: string]: string },
{ success = false, err }: { success?: boolean; err?: Error } = {},
) {
const obj: LogEventObject = { conf };
let message: string;
let eventName: string;
if (success) {
message = `Applied configuration change ${JSON.stringify(conf)}`;
eventName = 'Apply config change success';
} else if (err != null) {
message = `Error applying configuration change: ${err}`;
eventName = 'Apply config change error';
obj.error = err;
} else {
message = `Applying configuration change ${JSON.stringify(conf)}`;
eventName = 'Apply config change in progress';
}
logSystemMessage(message, obj, eventName);
}
function objectNameForLogs(eventObj: LogEventObject): string | null {
if (eventObj == null) {
return null;
}
if (
eventObj.service != null &&
eventObj.service.serviceName != null &&
eventObj.service.config != null &&
eventObj.service.config.image != null
) {
return `${eventObj.service.serviceName} ${eventObj.service.config.image}`;
}
if (eventObj.image != null) {
return eventObj.image.name;
}
if (eventObj.network != null && eventObj.network.name != null) {
return eventObj.network.name;
}
if (eventObj.volume != null && eventObj.volume.name != null) {
return eventObj.volume.name;
}
if (eventObj.fields != null) {
return eventObj.fields.join(',');
}
return null;
}

View File

@ -2,7 +2,7 @@ import * as apiBinder from './api-binder';
import * as db from './db';
import * as config from './config';
import * as deviceState from './device-state';
import * as logger from './logger';
import * as logger from './logging';
import SupervisorAPI from './device-api';
import * as v1 from './device-api/v1';
import * as v2 from './device-api/v2';

View File

@ -3,7 +3,7 @@ import type { SinonStub } from 'sinon';
import { stub } from 'sinon';
import { Volume } from '~/src/compose/volume';
import * as logTypes from '~/lib/log-types';
import * as logger from '~/src/logger';
import * as logger from '~/src/logging';
import Docker from 'dockerode';

View File

@ -7,7 +7,7 @@ import { expect } from 'chai';
import * as deviceConfig from '~/src/device-config';
import * as fsUtils from '~/lib/fs-utils';
import * as logger from '~/src/logger';
import * as logger from '~/src/logging';
import { Extlinux } from '~/src/config/backends/extlinux';
import { ConfigTxt } from '~/src/config/backends/config-txt';
import { Odmdata } from '~/src/config/backends/odmdata';

View File

@ -3,7 +3,7 @@ import { expect } from 'chai';
import sinon from 'sinon';
import * as config from '~/src/config';
import * as logger from '~/src/logger';
import * as logger from '~/src/logging';
import * as iptablesMock from '~/test-lib/mocked-iptables';
import * as dbFormat from '~/src/device-state/db-format';

View File

@ -9,7 +9,7 @@ import { setTimeout } from 'timers/promises';
import * as config from '~/src/config';
describe('Logger', function () {
let logger: typeof import('~/src/logger');
let logger: typeof import('~/src/logging');
let configStub: sinon.SinonStub;
beforeEach(async function () {

View File

@ -14,7 +14,7 @@ import * as images from '~/src/compose/images';
import * as config from '~/src/config';
import * as mockedDockerode from '~/test-lib/mocked-dockerode';
import * as applicationManager from '~/src/compose/application-manager';
import * as logger from '~/src/logger';
import * as logger from '~/src/logging';
describe('SupervisorAPI [V2 Endpoints]', () => {
let serviceManagerMock: SinonStub;