npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sap/logging

v8.3.0

Published

Provides logging and tracing functionalities for Node.js applications

Downloads

130,995

Readme

@sap/logging

A package that provides logging and tracing functionalities for Node.js applications.

Example

Following is an example of how to consume the package using express:

var logging = require('@sap/logging');
var express = require('express');

var app = express();

var appContext = logging.createAppContext();

app.use(logging.middleware({ appContext: appContext, logNetwork: true }));

app.get('/demo', function (req, res) {
  var logger = req.loggingContext.getLogger('/Application/Network');
  var tracer = req.loggingContext.getTracer(__filename);

  logger.info('Retrieving demo greeting ...');
  tracer.info('Processing GET request to /demo');

  res.send('Hello World!');
});

app.listen(3000, function() {
  console.log('Server started');
});

In general, you just need to:

  • Initialize the logging library with some application-wide options.
  • Use the provided middleware that will extract request specific information. It is recommended that this middleware is the first one to be called in order to have the logging context available as early as possible. It is also recommended to have the middleware that sets the user of the request (if the application intends to log the current user) right after the one provided by this library.
  • Instantiate a logger and a tracer via the loggingContext property of the request.
  • Log and trace whatever you need.

See more details below.

Logging & Tracing

Logs are addressed to an administrator of an application. Traces - to a developer or support staff.

  • Events that need to be logged are related to how the app operates - e.g. the app cannot display some results taken from a remote HTTP service because the remote server is down. An administrator of an app does not need to know how it is implemented, he/she should just be able to determine the state of the app itself.
  • Traces are mainly used when a problem has occurred and further investigation on code level has to take place.

The logging library writes log entries to standard output and trace entries - to standard error.

Categories & Locations

  • Categories - represent a feature area in an application. For example, different layers - Network layer, Database layer etc. The concept of categories is used in logging.
  • Locations - represent a location in the source code - e.g. a path to a file. Used in the context of tracing. Getting a tracer object requires explicitly providing a location. It is recommended to pass as argument the location of the current script - __filename.

Location values

The path to the application root is removed from the source file paths in the trace output to reduce duplication. The application root directory is taken from the HOME environment variable. If it is not defined, absolute file paths are used in the trace output. Example:

Let's assume the structure of your application looks like the following:

Here is how the location of a script (path to a file) will look like in the trace entries (having in mind __filename has been passed as location to the tracer):

| Script | HOME environment variable is set | HOME environment variable is not set | | ------ | ----------------------------------- | -------------------------------------- | | index.js | /lib/index.js | /path/from/root/demo-app/lib/index.js | | customer-service.js | /lib/services/customer-service.js | /path/from/root/demo-app/lib/services/customer-service.js |

The same applies to Windows systems as well.

Note: The path separator in the trace entries is always a forward slash, no matter the platform.

Wildcard support

The asterisk (*) is the only wildcard character supported by the library. It corresponds to zero or more characters (no matter what they are). Let's illustrate the wildcard usage using the example application structure from the previous section, assuming the HOME environment variable is set to the root of the application (as it would be on XS Advanced or Cloud Foundry):

| Pattern | Result | | ------- | ------ | | /Application/Network | Match a certain Category | | /Application/Network/* | Match all subcategories | | /lib/services/users-service.js | Match a specific file | | /lib/services/* | Match all files in all subdirectories of services |

Formats

  • ListLog format is used for logs in XS Advanced and during local development
  • Trace format is used for traces in XS Advanced and during local development
  • CF Log format is used for logs and traces in Cloud Foundry

Note: Instead of thread-name (in ListLog and Trace formats), a context id is used. This is an identifier that helps to distinguish which entries are logged/traced because of which requests/events. More info on that is available in this section.

Severity Levels

The following table shows which severity levels are available for loggers and tracers:

| Logging | Tracing | | ------- | ------- | | | debug | | | path | | info | info | | warning | warning | | error | error | | fatal | fatal |

Here you can find when to use which level:

| Level | When to use | | ----- | ----------- | | debug | Used to output the internal status of a program. | | path | Used to analyze the execution flow of a program. | | info | Used for events that do not need any follow up activity. They show the normal operations within an app. | | warning | Used for events that need follow up activity in order to prevent errors in the future. | | error | Used when the desired tasks cannot be completed and the application is still usable. | | fatal | Used in case of errors, because of which the application is no longer usable. |

The default severity level for loggers is info and the default one for tracers is error.

XS_APP_LOG_LEVEL environment variable

The XS_APP_LOG_LEVEL environment variable can be used to configure severity levels for logging and tracing. Valid values are severity levels from debug to fatal. The level specified in this environment variable will be used instead of all already set levels. none is also a valid value for XS_APP_LOG_LEVEL. In that case all logging and tracing is disabled (useful for automated tests). The library throws an error if the value of XS_APP_LOG_LEVEL is not a valid severity level or none.

Changing severity levels for an application

XS Advanced

You can enable debug logs and traces for an application deployed on XS Advanced via the command:

xs set-logging-level <application-name> "*" debug

This can be reverted via the command:

xs unset-logging-level <application-name> "*"

Restart of the application is not required for those commands to take effect.

The example above shows how to enable debug level for all loggers and tracers. It is possible to use the command for setting levels for a single category or location (e.g. xs set-logging-level <application-name> "/index.js" debug), or multiple by using the asterisk wildcard (e.g. xs set-logging-level <application-name> "/Application/Network/*" debug). The comparison with the actual category/location of a logger/tracer is executed in a case insensitive manner.

Cloud Foundry

You can enable debug logs and traces for an application deployed on Cloud Foundry via setting the environment variable XS_APP_LOG_LEVEL:

cf set-env <application-name> XS_APP_LOG_LEVEL debug

This can be reverted via the command:

cf unset-env <application-name> XS_APP_LOG_LEVEL

Note: Application restart is required after each of the commands above in order the changes to the environment to take effect.

Logging sensitive data

Logging sensitive data is not enabled by default. The following table shows which environment variable enables (when set to true) which fields and for which formats:

| Environment Variable | Field | Taken from | Format | | ------------------------ | ------- | ----------------------- | --------------- | | XS_LOG_USER | user/remote_user | req.user.id property | ListLog, CF Log | | XS_LOG_REFERER | referer | referer request header | CF Log | | XS_LOG_CONNECTION_DATA | remote_ip, remote_host, remote_port, x_forwarded_for | req.connection.remoteAddress property, req.connection.remotePort property, x-forwarded-for header | CF Log |

API

To consume the logging package, an application context needs to be created. It contains information that is valid for the whole application. Next a log context needs to be created. It contains information that is valid for the current context. A separate log context should be created for each new event (HTTP request received, job execution started, message from messaging service received). Because of the asynchronous nature of Node.js, entries produced during the processing of different events can be mixed. All entries contain information specific to the log context they are associated with, which helps to distinguish between entries produced during the processing of different events. Loggers and tracers are obtained from the log context.

Application context

var logging = require('@sap/logging');

var appContext = logging.createAppContext({
  // options
});

To create the application context, pass some application-wide options. Here is a list of the properties you may optionally pass:

| Property | Description | | ------------- | ----------- | | csnComponent | String Only applicable to SAP applications. |

You may use the application context to change severity levels (with wildcards for flexibility):

appContext.setLevel('/Application/*', 'warning'); // for a logger
appContext.setLevel(pathToFile, 'debug'); // for a tracer

The method throws an error in case of an incorrect level.

Severity levels can be unset with:

appContext.unsetLevel('/Application/*'); // for a logger
appContext.unsetLevel(pathToFile); // for a tracer

To set custom fields, you could do:

appContext.setCustomFields(["abc"]);

Log context

appContext.createLogContext({
  // options
});

A log context needs to be created in order to obtain a logger or a tracer.

Here is a list of the options that can be provided to the log context (all are optional):

| Option | Description | | ------------- | ----------- | | id | String Included in all logs and traces, should be unique. Used to distinguish entries from different log contexts. Defaults to an auto-generated value. If req is provided, the value is taken from the request headers x-request-id and x-vcap-request-id if present. It is recommended to explicitly pass an empty string for log contexts used during application startup. If req is present, then this id can be thought of as a request id, because all log/trace entries for that request will have the same id. See this section for more information. | | correlationId | String Used to correlate entries for a logical transaction which involves processing within different applications. If the value is not set explicitly, then it is taken from the x-correlationid header (if req is provided and the header is present) or from the id of the log context. | | req | Object Represents an HTTP request. |

The log context exposes the following functions:

| Function | Description | | --------------- | ----------- | | getAppContext | AppConext object associated with this log context |

The log context exposes the following read-only properties:

| Property | Description | | ------------- | ----------- | | id | The id of the log context. | | correlationId | The correlation id of the log context. This property is useful when the value needs to be sent to another application. |

Loggers and tracers can be obtained from the log context as follows:

let logger = logContext.getLogger('/Application/Network');
let tracer = logContext.getTracer(__filename);

It is possible to log request metrics for an HTTP request using the enableNetworkLog method:

logContext.enableNetworkLog(res);

It takes a response object as argument. A req object needs to be passed in advance as option when creating the log context. This method registers a handler for the finish event of the HTTP response. An entry (of info severity level), containing request metrics will be logged when the event is emitted. enableNetworkLog should be called once for a request-response pair:

http.createServer(function (req, res) {
  var reqContext = appContext.createLogContext({ req: req });
  reqContext.enableNetworkLog(res);

  res.end('Hello World');
});

The log entry uses category /LoggingLibrary/NetworkLog. It can be used to turn off the network log at runtime with xs set-logging-level or with appContext.setLevel.

Log context id

Let's take HTTP requests as an example: Because of the single-threaded nature and the event loop mechanism in Node.js, you may:

  • receive a request
  • start some async I/O operation
  • in the meantime start processing another request before returning a response to the first one

This means that the log entries for the two requests will be mixed a bit. To overcome this issue, each request (and the corresponding log context) is associated with a unique id which is present in the logs and traces for that request. In that way one can distinguish between the logs from the first request and the logs from the second request.

The same concept applies to messages received from a messaging service and to job runs triggered according to a schedule.

Middleware

There is a utility middleware that can be used (see this example). It automatically attaches a property named "loggingContext" to the request object. It accepts an object with properties:

| Property | Description | | ------------- | ----------- | | appContext | Mandatory An application context object. | | logNetwork | Optional Defaults to false, boolean specifying whether an entry containing request metrics will be logged for every finished HTTP request. |

The middleware sets the x-request-id response header to the context id for the current request, so in case of any troubles you may see the value in the response header and then filter the logs to see what the entries for that request are.

Loggers

You may create a logger in the following way:

var logger = req.loggingContext.getLogger('/Application/Network');

The log context has got the "getLogger" function that takes 1 string argument - the category. Categories are names of functional areas in an application. We recommend your categories to always begin with "/Application". The categories form a hierarchy with forward slash as a separator. Using back slashes in categories is not allowed.

You may always get the severity level (a string) of a logger with such code:

var level = logger.getLevel();

It is also possible to check whether an entry with a specific severity level will be logged with the current level configuration:

var willItBeLogged = logger.isEnabled('info');

Logging entries:

logger.info('Successful login of user %s - ', user, new Date());
logger.warning('Job could not finish successfully. An app admin should retrigger it.');
logger.error(new Error('Uups, an error has occurred'));
logger.fatal('We are in trouble');

You may use the same string interpolation mechanism as with util.format

Logging errors:

function callback(err, result) {
  if (err) {
    logger.error(err, 'Error during operation X');
  }
  // ...
}

If the first argument is an error, its message is appended to the log message. Also, the error stack is written to the trace. This works for all severity levels and also with tracers.

Tracers

All you need to do to obtain a tracer instance is:

var tracer = req.loggingContext.getTracer(__filename);

Methods regarding level getting and checking are provided (similarly to loggers):

var level = tracer.getLevel();
var willItBeTraced = tracer.isEnabled('path');
// etc.

Custom Fields

Note: This feature is available in Cloud Foundry and not in XS Advanced.

If you want to use custom fields, you need to set them to the application context. The expected format is a string based array:

      appContext.setCustomFields(["custom1", "custom2"]);

When logging, if the last argument is an object with a custom field, the custom field will be included in the log output as a custom field and not as part of the message:

app.get('/', function (req, res) {
  logger.info('Let me say hi ...' ,{
    "abc": "data"
  });
  // ... "custom_fields": {"abc": "data"} ...

Convenient tracing methods

Note: The first argument to all of these methods should be a string with the name of the function in which entries are being traced.

There are several methods that the API provides for convenience (they use severity level path):

  • entering - used to record that a function has been entered in the program flow. You may pass all of the arguments of your function to the entering function and they will be traced.
  • exiting - typically used in pair with the entering method. You may pass the return value of your function to the exiting function.
function myFunction(tracer, a, b ,c) {
  tracer.entering('myFunction', a, b, c);

  var result = // some logic here ...

  tracer.exiting('myFunction', result);
  return result;
}
  • throwing - used when you would like to trace when the code is about to throw an error. You may pass the error that is about to be thrown as an argument.
  • catching - used in catch blocks. You may pass the caught error as an argument.
function func1(tracer) {
  var error = new Error('An error has occurred');
  tracer.throwing('func1', error);
  throw err;
}

function func2(tracer) {
  try {
    func1(tracer);
  } catch (err) {
    tracer.catching('func2', err);
    // logic for processing the error
  }
}

Other

The library supports SAP Passports. When a log context is created with a request object that has the sap-passport header, the unique identifiers of the received SAP Passport will be part of the log entries for ListLog format.

Migration guide

Guide on how to adopt new major versions of the library can be found here.