Why Logging is Important

...and how Logality can help you with that

By Thanos - @thanpolas

Node.js

Hi, I am Thanos

  • Nearly 50 published NPM packages.
  • Over 100 OSS repos.
  • Contributor in major and small OSS projects.
  • Software Engineer, CTO, Founder, Gardener 👨‍🌾.
  • Founder of skgtech.io and devitconf.org.

Why Logging is Important

  • Debugging.
  • Error Tracing.
  • Performance Troubleshooting.
  • Accounting.
  • Audit Trail.
  • Security.

Debugging & Error Tracing

Having a log at the right place, with the right kind of information can save your 🥓🥓🥓.

Why Logging is Important

Debugging & Error Tracing

Why Logging is Important

Where and What to Log

  • Application Boot (OS, User, NODE_ENV).
  • Modules / Services Booting Up.
  • Node.js Error Handlers (SIGINT, uncaughtException).
  • Express & http Error Handlers.
  • All your error catch statements, if they don't bubble up.

Performance

Why Logging is Important

  • Timed logs provide performance insights by default.
  • Log time of execution.
  • Or, just log beginning and completion of operations.

DIY Performance Measuring

Accounting, Audit, Security

Why Logging is Important

  • All mutating operations.
  • Becomes an Audit Trail when saved on immutable store.
    • Highly Secure Application require audit trails on querying as well.
  • Use flags to tag log types (audit, security).
  • Beware of PII (emails, names) - tokenise data.

Where and What to Log

What and How to Log

  • Who am I?
  • Where am I?
  • What am I?
  • Metadata
  • Flags and Tagging

How To Log

What and How to Log

  • Pretty Print for local development.
  • Muted for automated testing.
  • JSON output for production.
[2021-04-10T13:05:32.712Z] ▶ notice /app/services.boot.js - Booting Services...
{"level":"notice","severity":5,"dt":"2021-04-10T13:04:03.215Z","message":"Booting Services...","context":{"runtime":{"application":"skgbot"},"source":{"file_name":"/app/services.boot.js"},"system":{"hostname":"192.168.1.74","pid":49862,"process_name":"/Users/thanpolas/.nvm/versions/node/v14.15.5/bin/node"}},"event":{}}

Pretty Print

JSON

Who Am I?

What and How to Log

  • What is my runtime?
  • What hostname am I on?
  • What is my process id?
  • What is my process name?

Where and What Am I?

What and How to Log

  • What is my logging Level & Severity?
  • What Module is the log from?
  • What function is this log from?
  • What time is it?

Flags and Tagging

What and How to Log

In a JSON logger, you should be able to add certain [boolean] flags so you can later easily query and filter for them.

Example Flags:

  • security: true
  • audit: true
  • bogus: true

How Logality Can Help

What is Logality?

Logality is a versatile and extensible logger for Node.JS

Logality Features

  • JSON and Pretty Print log messages.
  • Extend or alter the logging schema to fit your needs.
  • Customise built-in serialisers by overwriting them to create your own logging schema.
  • Middleware support.

Logality Features

  • Allows full manipulation of output.
  • Asynchronous operation.
  • Use in libraries and compose multiple Logality instances on the root project.
  • Automatically detects the module filename and path and includes them in the log.

JSON Output

Logality Features

{
    "severity": 6,
    "level": "info",
    "dt": "2018-05-18T16:25:57.815Z",
    "message": "hello world",
    "event": {},
    "context": {
        "runtime": {
            "application": "testLogality"
        },
        "source": {
          "file_name": "/test/spec/surface.test.js"
        },
        "system": {
            "hostname": "localhost",
            "pid": 36255,
            "process_name": "node ."
        }
    }
}

Automatic Detection

Pretty Print Output

Logality Features

Logality Uses the Syslog Severity Levels (RFC 5424)

Logality Serialisers

Logality Features

Serialisers are triggered by defined keys in the context object.

Each serialiser is configured to listen to a specific key.

log.info('User Logged in', {
    user: udo,
});

Built-In Serialisers

Logality Features

User Serialiser

log.info('User Logged in', { user: user })

Error Serialiser

log.error('User Logged in', { error: exception })

Express Request Serialiser

function index (req, res) {
  log.info('Index request', { req });
}

Custom Serialisers

Logality Features

You can define your own serialisers

const mySerialisers = {
  order: function (order) {
    return {
      path: 'context.order',
      value: {
        order_id: order.id,
        sku_id: order.sku,
        total_price: order.item_price * order.quantity,
        quantity: order.quantity,
      },
    };
  },
};
log.info('New order', {order: orderItem})

Custom + Built-In Serialisers

Logality Features

You can overwrite built-in serialisers

const mySerialisers = {
  user: function (user) {
    return {
      path: 'context.user',
      value: {
        id: user.id,
        email: user.email,
        first_name: user.first_name,
        last_name: user.last_name,
      },
    };
  },
};
log.info('User Logged In', {user: req.user})

Middleware Support

Logality Features

Middleware are invoked after serialisers

logality.use((context) => {
    delete context.user;
});

The "context" object is a JS Native Object, representing the entire log message.

Async Middleware Support

Logality Features

You can configure Logality for asynchronous operation

logality.use(async (context) => {
  await db.write(context);
});

Consequently, all logging commands need async invocation:

await log.info('Something happened');

Output Manipulation

Logality Features

You can fully manipulate the master output:

const Logality = require('logality');

const logality = Logality({
  appName: 'service-something',
  prettyPrint: false,
  async: false,
  output: (logContext) => {
    const logMessage = JSON.stringify(logContext);
    process.stdout.write(logMessage);
  },
});

The "logContext" object is a JS Native Object, representing the entire log message.

Use in Libraries

Logality Features

Logality can safely be used in libraries!

const thirdPartyLib = require('thirdPartyLibrary');

/** ... */

const myLogality = Logality();

myLogality.pipe(thirdPartyLib.logality);

Other Logging Libraries

Other Logging Libraries

Winston

Pino.js

Bunyan

Comparison

Logality Winston Bunyan Pino
JSON Output
Pretty Print
Custom Log Levels ​❌ ​✅ ​❌ ​✅
Serialisers ​❌
Middleware ​❌
Mutate JSON Schema

Comparison

Logality Winston Bunyan Pino
Output Destination
Mutate Output ​✅ ​❌ ​❌
Async Operation ​❌ ​❌ ​❌
Filename Detection ​✅ ​​❌ ​​❌ ​​❌
Speed Optimised ​​❌ ​​❌ ​​❌
Used in Libraries ​​❌ ​​❌ ​​❌

Recap

  • Logging is important for:
    • Debugging and troubleshooting.
    • Security and Audit.
    • Performance monitoring.
  • Appropriate Logs can make all the difference.
  • You have many choices for a logger, choose what is appropriate for you.

Thank you

Thanos Polychronakis

@thanpolas

https://speakerdeck.com/thanpolas

Thank you

Thanos Polychronakis

@thanpolas

https://speakerdeck.com/thanpolas

Questions?

Made with Slides.com