# Pino (Logging)

{% code title="main.ts" %}

```typescript

// bufferLogs: true is a feature that temporarily stores (buffers) 
// all log messages that occur during the application's bootstrap process 
// until the logger is fully configured. Here's why this is important
// prevent logging loss
const app = await NestFactory.create(AppModule, { bufferLogs: true });
// Uses dependency injection to get the logger instance (app.get(Logger))
app.useLogger(app.get(Logger));
```

{% endcode %}

{% code title="app.module.ts" %}

```typescript
@Module({
  imports: [
   // Declare logger module in global level and customize the setting
    LoggerModule.forRoot({
      pinoHttp: pinoOptionConfig(),
    })
  ],
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
```

{% endcode %}

{% code title="pino.config.ts" %}

```typescript
import { Options } from 'pino-http';
import pino from 'pino';
import * as apm from 'elastic-apm-node/start';
const LOG_FILE_BASE_PATH = process.env.LOG_FILE_BASE_PATH ?? '/tmp';
const LOG_FILE_ROTATE_SIZE = process.env.LOG_FILE_ROTATE_SIZE ?? '50M';
const LOG_FILE_MAX_FILE_HISTORY = process.env.LOG_FILE_MAX_FILE_HISTORY ?? 5;

type TransportOptions =
  | pino.TransportSingleOptions<Record<string, any>>
  | pino.TransportMultiOptions<Record<string, any>>
  | pino.TransportPipelineOptions<Record<string, any>>;

const targetOptions = {
  translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l o',
  ignore: 'res,context,filename',
  singleLine: true,
};

export function pinoOptionConfig(): Options {
  const logLevel = process.env.BOT_BUILDER_BACKEND_LOG_LEVEL || 'info';
  return {
   // min level of log
   // Only logs at this level 
   // or higher will be output (e.g., if set to 'info', debug logs won't show)
    level: logLevel,
   // append the additional field
    mixin: (mergeObject, level) => {
      return { 'level-label': pino.levels.labels[level].toUpperCase(), ...injectApmTraceId() };
    },
    // time of each log
    timestamp: pino.stdTimeFunctions.isoTime,
    // autom log http res and req
    autoLogging: true,
    quietReqLogger: false,
    // the output setting
    transport: configTransport(logLevel),
    // format specific log, such as request
    serializers: {
      req: formatRequestLog,
    },
  };
}

const injectApmTraceId = () => {
  const apmTransaction = {};
  if (apm?.isStarted()) {
    const apmTx = apm.currentTransaction;
    if (apmTx) {
      const apmCurrentTransaction = {};
      apmCurrentTransaction['trace.id'] = apmTx.ids['trace.id'];
      apmCurrentTransaction['transaction.id'] = apmTx.ids['transaction.id'];
      const span = apm.currentSpan;
      if (span) {
        apmCurrentTransaction['span.id'] = span.ids['span.id'];
      }
      apmTransaction['apmTransaction'] = apmCurrentTransaction;
    }
  }
  return apmTransaction;
};

const formatRequestLog = (req: pino.SerializedRequest) => {
  const requestLog: { api: string; gravitee: Record<string, string> | null } = {
    api: `[${req.id}] [${req.method}] [${req.url}]`,
    gravitee: null,
  };
  if (req.headers['x-gravitee-request-id']) {
    requestLog.gravitee = {
      'x-gravitee-request-id': req.headers['x-gravitee-request-id'],
      'x-gravitee-transaction-id': req.headers['x-gravitee-transaction-id'],
    };
  }
  return requestLog;
};

const configTransport = (logLevel: string): TransportOptions => {
  return {
    // console log output setting
    targets: [
      {
        target: 'pino-pretty',
        level: logLevel,
        options: {
          ...targetOptions,
        },
      },
      // file log output setting
      {
        target: require.resolve('./pino-transport-file-rotating'),
        level: logLevel,
        options: {
          size: LOG_FILE_ROTATE_SIZE,
          maxFiles: Number(LOG_FILE_MAX_FILE_HISTORY),
          path: LOG_FILE_BASE_PATH,
        },
      },
    ],
  };
};

```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://petercheng7788.gitbook.io/developer-note/backend/nodejs/pino-logging.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
