Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions api/utils/log.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
const pino = require('pino');

// Optional pino-pretty import for human-readable console output
let pinoPretty;
try {
pinoPretty = require('pino-pretty');
}
catch (e) {
// pino-pretty not available, will use JSON output
}

// Optional OpenTelemetry imports
let trace;
let context;
Expand Down Expand Up @@ -207,7 +216,12 @@ class LogManager {
this.#prefs = loadLoggingConfig();
this.#prefs.default = this.#prefs.default || 'warn';
this.#deflt = this.#prefs.default || 'error';
this.#prettyPrint = this.#prefs.prettyPrint || false;
// Output format determined by: explicit config > environment detection
// - prettyPrint: true → always pretty (human-readable)
// - prettyPrint: false → always JSON (for log aggregation)
// - prettyPrint: unset → auto-detect (pretty in terminal/CI, JSON in Docker/PM2/pipes)
const isInteractive = process.stdout.isTTY || !!process.env.CI;
this.#prettyPrint = this.#prefs.prettyPrint ?? isInteractive;

// Initialize OpenTelemetry metrics if available
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this but please dont enable this by default.
This is only good for dev envs, since most of our logs are now json ( the ones that are not will be converted ) They will be human readable for prod using grafana or other tools.

The other issue with pretty is it works with event emitter and since countly has huge number of subloggers (plugins,modules each have their own) This can cause memory leaks and similar issues

ref: pinojs/pino-pretty#387
ref: pinojs/pino#144

Lets keep this but not enable by default, or by default when NODE_ENV === development or similar?

if (metrics) {
Expand Down Expand Up @@ -306,22 +320,19 @@ class LogManager {
}

/**
* Gets or creates the pretty transport singleton
* @returns {Object|null} The pretty transport or null
* Gets or creates the pretty stream singleton
* @returns {Object|null} The pretty stream or null
*/
getPrettyTransport() {
if (!this.#prettyPrint) {
if (!this.#prettyPrint || !pinoPretty) {
return null;
}

if (!this.#prettyTransport) {
this.#prettyTransport = pino.transport({
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
ignore: 'pid,hostname'
}
this.#prettyTransport = pinoPretty({
colorize: true,
translateTime: 'SYS:HH:MM:ss.l',
ignore: 'pid,hostname'
});
}
return this.#prettyTransport;
Expand Down
46 changes: 2 additions & 44 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@types/underscore": "^1.13.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"debug": "^4.4.3",
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug is required at runtime from plugins/pluginManager.ts, but it's being added under devDependencies. If Countly is installed with npm ci --omit=dev / production-only installs, require('debug') will throw and the plugin manager will crash on startup. Move debug to dependencies or make the import optional (fallback to a no-op logger).

Copilot uses AI. Check for mistakes.
"docdash": "^2.0.1",
"env-cmd": "^10.1.0",
"eslint": "^8.56.0",
Expand Down
13 changes: 8 additions & 5 deletions plugins/pluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ const async: any = require('async');
const _: any = require('underscore');
const crypto: typeof import('crypto') = require('crypto');
const BluebirdPromise: any = require('bluebird');
const debug: any = require('debug');
const log: LogModule = require('../api/utils/log.js');
const logDbRead: Logger = log('db:read');
const logDbWrite: Logger = log('db:write');
Expand All @@ -199,6 +200,7 @@ const exec: typeof cp.exec = cp.exec;
const spawn: typeof cp.spawn = cp.spawn;
const configextender: ConfigExtenderFn = require('../api/configextender');

const d = debug('countly:pluginManager');
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says verbose plugin load errors are available via DEBUG=countly:plugins, but the code uses debug('countly:pluginManager'). Either switch the namespace to match the documented env var, or update the PR/docs so developers know which DEBUG value to set.

Copilot uses AI. Check for mistakes.
// ============================================================
// INTERNAL INTERFACES (used within this module only)
// ============================================================
Expand Down Expand Up @@ -436,9 +438,9 @@ class PluginManager {
}
}
catch (ex: any) {
console.log('Skipping plugin ' + pluginNames[i] + ' as we could not load it because of errors.');
console.error(ex.stack);
console.log('Saving this plugin as disabled in db');
const errorLine = (ex.message || '').split('\n')[0];
console.log('Skipping plugin ' + pluginNames[i] + ': ' + errorLine);
d('Plugin %s load error:\n%s', pluginNames[i], ex.stack);
}
}
}
Expand Down Expand Up @@ -1272,8 +1274,9 @@ class PluginManager {
}
}
catch (ex: any) {
console.log('skipping plugin because of errors:' + pluginNames[i]);
console.error(ex.stack);
const errorLine = (ex.message || '').split('\n')[0];
console.log('Skipping plugin ' + pluginNames[i] + ': ' + errorLine);
d('Plugin %s load error:\n%s', pluginNames[i], ex.stack);
}
}
}
Expand Down
Loading