Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: replace bunyan logger #1447

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open

fix: replace bunyan logger #1447

wants to merge 25 commits into from

Conversation

abhilash-sivan
Copy link
Contributor

@abhilash-sivan abhilash-sivan commented Nov 13, 2024

  • lock file change
  • check and verify concatenated strings using %s and correct it
  • pass logs to agent stream
  • fix tests for pino and logger_test
  • fix test for pino/test
  • fix instrumentation - We don't want to trace Instana's own loggers ( __in)
  • add additional test cases in pino
  • remove unwanted tests and enable only simple app test in bunyan

@abhilash-sivan abhilash-sivan changed the title Fix replace bunyan fix: replace bunyan logger Nov 18, 2024
@@ -267,9 +267,11 @@ exports.sendSpans = function sendSpans(spans, cb) {
if (err && !maxContentErrorHasBeenLogged && err instanceof PayloadTooLargeError) {
logLargeSpans(spans);
} else if (err) {
logger.debug('Failed to send: %s', getSpanLengthInfo(spans));
const obj = getSpanLengthInfo(spans);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pino does not support concatenated strings using %s if an object is passed instead of a string.

Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Please use

logger.debug('Failed to send: %o', getSpanLengthInfo(spans));

There is more occurrences in the codebase.

Copy link
Contributor

Choose a reason for hiding this comment

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

logger.debug('Failed to send: %o', getSpanLengthInfo(spans));

Correction: This won't work for custom bunyan logger or winston.
Lets not use %s and no %o at all. Only normal strings.

If that works for all logger types, we can use this:

logger.debug('Failed to send spans", getSpanLengthInfo(spans))

If that does not work, you can use:

blabla = getSpanLengthInfo()
logger.debug(Failed to send spans ${blabla});

This is what you did already. Its probably better to raise a small PR to just remove this from our codebase. That would be good and makes the review easier.

* Custom write method to send logs to multiple destinations
* @param {string} chunk
*/
write(chunk) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

custom write method which writes the log to stdout and agent stream function (pinoToAgentStream)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried implementing this use case using pino.transport, pino.destination and pino.multistream etc. And finally ended up with the current implementation which is working as expected like previous bunyan stream

@@ -31,40 +31,57 @@ const registry = {};
* @param {boolean} [isReInit]
*/
exports.init = function init(config, isReInit) {
const consoleStream = pino.destination(1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If multistream is used, we need to specify the log streams where it is going. Here this is defined for logging into the process.stdout

Copy link
Contributor

Choose a reason for hiding this comment

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

I can't follow why we define this here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

While using multistream, our logger did not write to the console(stdout) by default.
We explicitly need to specify wherever this logs should go.
So added stdout and the agentStream as two attributes

parentLogger = pino(
{
...parentLogger.levels,
base: parentLogger.bindings()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if we want to change something in pino logger after it is initialized, we need to create a new logger and assign initial logger bindings to it

@@ -8,18 +8,18 @@ const graphQL = require('graphql');
const graphqlSubscriptions = require('graphql-subscriptions');
const data = require('./data');
// Pino log spans are used to verify that follow up calls are traced correctly in a GraphQL entry.
const pinoLogger = require('pino')();
const bunyanLogger = require('bunyan').createLogger({ name: 'test-logger' });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason for not using Winston here is that it is added to the list of packages allowed to load before the core module. In these situations, we might lose some spans that can cause the test to fail. We don't want to re-run the tests suppose this situation occurs

@@ -16,7 +16,7 @@ const instana = require('../../../..')();
const bodyParser = require('body-parser');
const express = require('express');
const morgan = require('morgan');
const pino = require('pino')();
const bunyan = require('bunyan').createLogger({ name: 'test-app' });
Copy link
Contributor

Choose a reason for hiding this comment

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

qs: Why can't we keep pino logger in all the apps? 🤔

@@ -32,7 +32,7 @@ const TeeLogger = require('../../../../../test_util/TeeLogger');
const { sqs } = require('./sqsUtil');

const instanaLogger = require('../../../../../../src/logger').getLogger('SQS receiver');
instanaLogger.level('warn');
instanaLogger.level = 'warn';
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Please use an interface method e.g. setInstanaLogLevel or simply setLevel. Otherwise we always need to update the whole codebase if we want to switch a logger.

@@ -267,9 +267,11 @@ exports.sendSpans = function sendSpans(spans, cb) {
if (err && !maxContentErrorHasBeenLogged && err instanceof PayloadTooLargeError) {
logLargeSpans(spans);
} else if (err) {
logger.debug('Failed to send: %s', getSpanLengthInfo(spans));
const obj = getSpanLengthInfo(spans);
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Please use

logger.debug('Failed to send: %o', getSpanLengthInfo(spans));

There is more occurrences in the codebase.

if (config.logger && typeof config.logger.child === 'function') {
// A bunyan or pino logger has been provided via config. In either case we create a child logger directly under the
// given logger which serves as the parent for all loggers we create later on.
// A pino logger has been provided via config; create a child logger directly under it.
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Why did you change the comment when the condition was not changed? Is config.logger.child === function not valid for Bunyan? Was this wrong in the first place?

level: 'info'
});

if (isPino(parentLogger)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

qs: It seems this condition is only for the internal logger, is that right? Because if yes, we can simplify the logic a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, this is for the internal logger only.

@@ -31,40 +31,57 @@ const registry = {};
* @param {boolean} [isReInit]
*/
exports.init = function init(config, isReInit) {
const consoleStream = pino.destination(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't follow why we define this here.

* @param {*} _logger
* @returns {boolean}
*/
function isPinoLogger(_logger) {
Copy link
Contributor

Choose a reason for hiding this comment

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

qs: Can we expose this helper function from the logger interface?

forceTransmissionStartingAt: 1
}
});
require('../../../..')();
Copy link
Contributor

Choose a reason for hiding this comment

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

qs: Can you please explain this removal?

const globalAgent = require('../../../globalAgent');
const appControls = require('./controls');
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Please do not touch this test. Add a new internal logging integration test.

@abhilash-sivan abhilash-sivan marked this pull request as ready for review December 20, 2024 10:21
@abhilash-sivan abhilash-sivan requested a review from a team as a code owner December 20, 2024 10:21
Copy link
Contributor

@aryamohanan aryamohanan left a comment

Choose a reason for hiding this comment

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

Why can't we use the native console instead of Pino?


'use strict';

// instana/no-unsafe-require
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// instana/no-unsafe-require

const logger = require('pino');

function createCustomLogger() {
const customPino = Object.assign(function (/** @type {any} */ ...args) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const customPino = Object.assign(function (/** @type {any} */ ...args) {
const customLogger = Object.assign(function (/** @type {any} */ ...args) {

@@ -29,13 +29,6 @@ function instrument(Logger) {
function shimLog(markAsError) {
return originalLog =>
function () {
if (arguments.length === 0 || (this.fields && !!this.fields.__in)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

qs: Why was the arguments.length === 0 check removed? This check prevents tracing unnecessary log calls like logger.warn() without arguments, which don’t produce useful data. How are we handling these cases now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was removed by mistake. Added it back

@@ -267,9 +267,11 @@ exports.sendSpans = function sendSpans(spans, cb) {
if (err && !maxContentErrorHasBeenLogged && err instanceof PayloadTooLargeError) {
logLargeSpans(spans);
} else if (err) {
logger.debug('Failed to send: %s', getSpanLengthInfo(spans));
const obj = getSpanLengthInfo(spans);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Consider renaming obj to spanInfo or a similar name to improve clarity and provide better context.

@@ -267,9 +267,11 @@ exports.sendSpans = function sendSpans(spans, cb) {
if (err && !maxContentErrorHasBeenLogged && err instanceof PayloadTooLargeError) {
logLargeSpans(spans);
} else if (err) {
logger.debug('Failed to send: %s', getSpanLengthInfo(spans));
const obj = getSpanLengthInfo(spans);
logger.debug({ obj }, 'Failed to transmit.');
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
logger.debug({ obj }, 'Failed to transmit.');
logger.debug('Failed to send', { obj });

Is this format works?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No this does not work with pino

@@ -16,12 +16,14 @@ try {
// thread (0).
}

const bunyan = require('bunyan');
const { logger } = require('@instana/core');
const { uninstrumentedLogger: pinoCopy } = require('@instana/core');
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const { uninstrumentedLogger: pinoCopy } = require('@instana/core');
const { uninstrumentedLogger: pinoCopy } = require('@instana/core');
Suggested change
const { uninstrumentedLogger: pinoCopy } = require('@instana/core');
const { uninstrumentedLogger: pino } = require('@instana/core');

consoleStream.write(chunk);

const logRecord = JSON.parse(chunk);
pinoToAgentStream.write(logRecord);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pinoToAgentStream.write(logRecord);
pinoToAgentStream.write(JSON.parse(chunk));

* @returns {boolean}
*/
function isBunyan(_logger) {
return _logger instanceof bunyan;
function isPino(_logger) {
Copy link
Contributor

Choose a reason for hiding this comment

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

concern: Relying on internal symbols (e.g., pino.symbols.streamSym)
may introduce risks if Pino's internal structure changes in future versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't check instanceof of pino. We need to rely on some internal functions to check this.
We don't need this check for the new logic. removing the function

@@ -42,7 +42,7 @@ if (process.env.SCREW_AROUND_WITH_UP_ARRAY_FIND) {
const instana = require('../../..')(config);

const http = require('http');
const pino = require('pino')();
const bunyan = require('bunyan').createLogger({ name: 'test-common' });
Copy link
Contributor

Choose a reason for hiding this comment

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

In tests, consider using Winston or any other logger instead of Bunyan, as Bunyan is no longer actively maintained. If we need to drop support for Bunyan in the future, it could become difficult.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Planning to get it back to pino itself, that way we can be sure that our internal pino logs are not traced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants