2 min read

Using Pino as logging service in the Dapr JS SDK

Dapr JS SDK enables custom logging service for Pino apps through simple initiation and implementation of custom interface. Learn how you can do so in this article!
Using Pino as logging service in the Dapr JS SDK

So, you are writing an application and you are used to work with pino? Luckily for us, the Dapr JS SDK allows you to implement your own logging service! This way, we can have all logging standardized.

The JS SDK has a documentation page at https://docs.dapr.io/developing-applications/sdks/js/js-logger/ that explains how we can do this. Simply initiate your logger and implement the custom interface!

Example

DaprLoggerService.ts

💡 I like to utilize the "component" key to describe which component is printing a certain log message.
import { LoggerService } from "@dapr/dapr";
import { Logger } from "pino";

export class DaprLogger implements LoggerService {
  private readonly logger: Logger;

  constructor(logger: Logger) {
    this.logger = logger;
  }

  error(message: any, ...optionalParams: any[]): void {
    this.logger.error({ component: "dapr" }, message, ...optionalParams);
  }

  warn(message: any, ...optionalParams: any[]): void {
    this.logger.warn({ component: "dapr" }, message, ...optionalParams);
  }

  info(message: any, ...optionalParams: any[]): void {
    this.logger.info({ component: "dapr" }, message, ...optionalParams);
  }

  verbose(message: any, ...optionalParams: any[]): void {
    this.logger.trace({ component: "dapr" }, message, ...optionalParams);
  }

  debug(message: any, ...optionalParams: any[]): void {
    this.logger.debug({ component: "dapr" }, message, ...optionalParams);
  }
}

index.ts

import { CommunicationProtocolEnum, DaprServer } from "@dapr/dapr";
import Config from "./config";
import pino from "pino";

import { DaprLogger } from "./DaprLoggerService";

const logger = pino();

async function start() {
  logger.info("Starting Dapr Server");
  const server = new DaprServer(
    Config.app.host,
    `${Config.app.port}`,
    Config.dapr.sidecar.host,
    Config.dapr.sidecar.portHttp,
    CommunicationProtocolEnum.HTTP,
    {
      logger: {
        service: new DaprLogger(logger),
      },
    }
  );

  await server.start();

  logger.info("Application Started");
}
start().catch(async (e) => {
  logger.error(e);
  process.exit(1);
});

Result

If we now run our application, we will see the following:

== APP == [nodemon] 2.0.20
== APP == [nodemon] to restart at any time, enter `rs`
== APP == [nodemon] watching path(s): src/**/*
== APP == [nodemon] watching extensions: ts,js
== APP == [nodemon] starting `ts-node --swc ./src/index.ts`
== APP == {"level":30,"time":1675103613502,"pid":8103,"hostname":"THE-BEAST","msg":"Starting Dapr Server"}
== APP == {"level":30,"time":1675103613516,"pid":8103,"hostname":"THE-BEAST","msg":"Adding Dapr Binding Handlers"}
== APP == {"level":30,"time":1675103613519,"pid":8103,"hostname":"THE-BEAST","type":"dapr","msg":"[HTTPServer, HTTPServer] Listening on 10000"}
== APP == {"level":30,"time":1675103613519,"pid":8103,"hostname":"THE-BEAST","type":"dapr","msg":"[HTTPServer, HTTPServer] Registering undefined PubSub Subscriptions"}
== APP == {"level":30,"time":1675103613526,"pid":8103,"hostname":"THE-BEAST","type":"dapr","msg":"[HTTPClient, HTTPClient] Awaiting Sidecar to be Started"}
INFO[0002] starting Dapr Runtime -- version 1.9.5 -- commit f5f847eef8721d85f115729ee9efa820fe7c4cd3  app_id=scheduler instance=THE-BEAST scope=dapr.runtime type=log ver=1.9.5
...
INFO[0003] dapr initialized. Status: Running. Init Elapsed 94ms  app_id=scheduler instance=THE-BEAST scope=dapr.runtime type=log ver=1.9.5
== APP == {"level":30,"time":1675103616049,"pid":8103,"hostname":"THE-BEAST","type":"dapr","msg":"[HTTPClient, HTTPClient] Sidecar Started"}
== APP == {"level":30,"time":1675103616049,"pid":8103,"hostname":"THE-BEAST","msg":"Application Started"}