|
| 1 | +# Logger |
| 2 | + |
| 3 | +The middleware application provides a logger instance that automatically attaches metadata related to the scope of each call. By adding this contextual data, the logger makes it significantly easier to trace the origin of logs or errors, simplifying the debugging and monitoring process across the application. |
| 4 | + |
| 5 | +:::info |
| 6 | +The logger has been available since version 5.1.0. Please refer to the [middleware changelog](https://docs.alokai.com/middleware/reference/change-log#_510) for more details. |
| 7 | +::: |
| 8 | + |
| 9 | +## Using logger |
| 10 | + |
| 11 | +The middleware application provides access to the logger in various parts of the system, such as in extensions and integrations. This flexibility allows you to log important events, errors, and other data throughout the lifecycle of your application. |
| 12 | + |
| 13 | +In this section, we will explore how to access and use the logger in different parts of the application. |
| 14 | + |
| 15 | +The examples below demonstrate how to use the application logger to ensure that correct metadata is conveyed in the logs. To achieve that, we will use the `getLogger` function to extract the logger instance from the passed argument. |
| 16 | + |
| 17 | +:::tip |
| 18 | +The logger is integrated into the middleware to provide additional metadata about the scope of call with every log out of the box. In case of error, it also provides an error boundary. |
| 19 | +::: |
| 20 | + |
| 21 | +### Basic Logger usage |
| 22 | + |
| 23 | +To use the logger, you need to import the `getLogger` function from the middleware package. Depending on the context, you can obtain the logger from different sources, such as the `alokai` object, `context` object, or `params` object. All such cases are covered in the following examples. For this example, we will use the `context` object. |
| 24 | + |
| 25 | +```ts |
| 26 | +import { getLogger } from "@vue-storefront/middleware"; |
| 27 | + |
| 28 | +// Assume that `context` is available in the current scope |
| 29 | +const logger = getLogger(context); |
| 30 | +logger.info("Middleware is running!"); |
| 31 | +``` |
| 32 | + |
| 33 | +Prints: |
| 34 | + |
| 35 | +```json |
| 36 | +{ |
| 37 | + "message": "Middleware is running!", |
| 38 | + "timestamp": "2024-10-22T19:04:18.791Z", |
| 39 | + "severity": "INFO", |
| 40 | + "alokai": { |
| 41 | + "context": "middleware", |
| 42 | + "scope": { |
| 43 | + // ... |
| 44 | + } |
| 45 | + } |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +The provided logger has a second optional argument that allows you to include custom metadata to be attached to the JSON output. |
| 50 | +For example: |
| 51 | + |
| 52 | +```ts |
| 53 | +logger.info("I've just fetched a user!", { |
| 54 | + userId: "123", |
| 55 | +}); |
| 56 | +``` |
| 57 | + |
| 58 | +Prints: |
| 59 | + |
| 60 | +```json |
| 61 | +{ |
| 62 | + "message": "I've just fetched a user!", |
| 63 | + "timestamp": "2024-10-22T19:04:18.791Z", |
| 64 | + "severity": "INFO", |
| 65 | + "alokai": { |
| 66 | + "context": "middleware", |
| 67 | + "scope": { |
| 68 | + // ... |
| 69 | + } |
| 70 | + }, |
| 71 | + "userId": "123" |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +:::warning |
| 76 | +It's forbidden to overwrite **alokai** key within provided metadata. Any attempt will be ineffective. If you need to provide additional metadata, use a different key. **alokai** key is reserved for internal use only. |
| 77 | +::: |
| 78 | + |
| 79 | +### `extendApp` |
| 80 | + |
| 81 | +```ts |
| 82 | +import { getLogger } from "@vue-storefront/middleware"; |
| 83 | + |
| 84 | +const someExtension = { |
| 85 | + name: "some-extension", |
| 86 | + extendApp(params) { |
| 87 | + const logger = getLogger(params); |
| 88 | + |
| 89 | + logger.info( |
| 90 | + "You can call a logger inside extendApp hook, called on bootstrap of the application" |
| 91 | + ); |
| 92 | + }, |
| 93 | +}; |
| 94 | +``` |
| 95 | + |
| 96 | +### Hooks |
| 97 | + |
| 98 | +Logger is available in hooks factory: |
| 99 | + |
| 100 | +```ts |
| 101 | +import { getLogger } from "@vue-storefront/middleware"; |
| 102 | + |
| 103 | +const paypalExtension = { |
| 104 | + name: "sapcc-paypal-extension", |
| 105 | + hooks(req, res, alokai) { |
| 106 | + const logger = getLogger(alokai); |
| 107 | + logger.info( |
| 108 | + "You can call a logger every time a hooks factory gets invoked" |
| 109 | + ); |
| 110 | + return { |
| 111 | + // ... |
| 112 | + }; |
| 113 | + }, |
| 114 | +}; |
| 115 | +``` |
| 116 | + |
| 117 | +In order, to use logger in certain hooks like `beforeCall`, `beforeCreate`, `afterCall`, `afterCreate` obtain it from it's params: |
| 118 | + |
| 119 | +```ts |
| 120 | +import { getLogger } from "@vue-storefront/middleware"; |
| 121 | + |
| 122 | +const paypalExtension = { |
| 123 | + name: "sapcc-paypal-extension", |
| 124 | + hooks(req, res, alokai) { |
| 125 | + return { |
| 126 | + beforeCall(params) { |
| 127 | + const logger = getLogger(params); |
| 128 | + logger.info("You can log every time a beforeCall hook gets invoked"); |
| 129 | + return params.args; |
| 130 | + }, |
| 131 | + beforeCreate(params) { |
| 132 | + const logger = getLogger(params); |
| 133 | + logger.info("You can log every time a beforeCreate hook gets invoked"); |
| 134 | + return params.configuration; |
| 135 | + }, |
| 136 | + afterCall(params) { |
| 137 | + const logger = getLogger(params); |
| 138 | + logger.info("You can log every time a afterCall hook gets invoked"); |
| 139 | + return params.response; |
| 140 | + }, |
| 141 | + afterCreate(params) { |
| 142 | + const logger = getLogger(params); |
| 143 | + logger.info("You can log every time a afterCreate hook gets invoked"); |
| 144 | + return params.configuration; |
| 145 | + }, |
| 146 | + }; |
| 147 | + }, |
| 148 | +}; |
| 149 | +``` |
| 150 | + |
| 151 | +#### Caveat: Accessing logger via closure |
| 152 | + |
| 153 | +Consider the following snippet: |
| 154 | + |
| 155 | +```ts |
| 156 | +import { getLogger } from "@vue-storefront/middleware"; |
| 157 | + |
| 158 | +const someExtension = { |
| 159 | + name: "some-extension", |
| 160 | + hooks(req, res, alokai) { |
| 161 | + const hooksLogger = getLogger(alokai); |
| 162 | + hooksLogger.info( |
| 163 | + "You can call a logger every time a hooks factory gets invoked" |
| 164 | + ); |
| 165 | + return { |
| 166 | + beforeCall(params) { |
| 167 | + // Never access via closure a logger belonging to the hooks factory: |
| 168 | + hooksLogger.info("Don't do that!"); |
| 169 | + // Instead use own logger of every hook function: |
| 170 | + const logger = getLogger(params); |
| 171 | + logger.info("Do that!"); |
| 172 | + return params.args; |
| 173 | + }, |
| 174 | + }; |
| 175 | + }, |
| 176 | +}; |
| 177 | +``` |
| 178 | + |
| 179 | +:::warning |
| 180 | +Attempt of using `hooksLogger` inside `beforeCall` or other hooks via closure corrupts information about scope of call of the log. Always use logger provided in params to the hook. |
| 181 | +::: |
| 182 | + |
| 183 | +### extendApiMethods |
| 184 | + |
| 185 | +Inside extended api methods, obtain logger from `context` using `getLogger` function. |
| 186 | + |
| 187 | +```ts |
| 188 | +import { getLogger } from "@vue-storefront/middleware"; |
| 189 | + |
| 190 | +const testingExtension = { |
| 191 | + name: "some-extension", |
| 192 | + extendApiMethods: { |
| 193 | + addedCustomEndpoint(context) { |
| 194 | + const logger = getLogger(context); |
| 195 | + logger.info("You can log inside newly created handler!"); |
| 196 | + return { |
| 197 | + // ... |
| 198 | + }; |
| 199 | + }, |
| 200 | + }, |
| 201 | +}; |
| 202 | +``` |
| 203 | + |
| 204 | +:::tip |
| 205 | +You can derive logger from context the same way if you are creating an integration. |
| 206 | +::: |
| 207 | + |
| 208 | +### onCreate |
| 209 | + |
| 210 | +The hook got a new parameter from which you can derive the logger with help of `getLogger` function. |
| 211 | + |
| 212 | +```ts |
| 213 | +import { type AlokaiContainer, getLogger } from "@vue-storefront/middleware"; |
| 214 | + |
| 215 | +const onCreate = async ( |
| 216 | + config: Record<string, unknown> = {}, |
| 217 | + alokai: AlokaiContainer |
| 218 | +) => { |
| 219 | + const logger = getLogger(alokai); |
| 220 | + logger.info("You can log inside onCreate when creating a new integration"); |
| 221 | + |
| 222 | + return { |
| 223 | + // ... |
| 224 | + }; |
| 225 | +}; |
| 226 | +``` |
| 227 | + |
| 228 | +## Configuration |
| 229 | + |
| 230 | +The middleware logger offers flexible configuration options to control logging behavior across the application. These options allow you to manage the verbosity and level of detail in logs based on the needs of different environments, such as development or production. |
| 231 | + |
| 232 | +### Available Configuration Options |
| 233 | + |
| 234 | +```ts |
| 235 | +/** |
| 236 | + * Options for the logger. |
| 237 | + */ |
| 238 | +export interface LoggerOptions { |
| 239 | + /** |
| 240 | + * The log level aligned with RFC5424. |
| 241 | + */ |
| 242 | + verbosity?: LogVerbosity; // (def. "info") |
| 243 | + |
| 244 | + /** |
| 245 | + * Whether to include the stack trace in the log message. |
| 246 | + */ |
| 247 | + includeStackTrace?: boolean; // (def. true) |
| 248 | +} |
| 249 | +``` |
| 250 | + |
| 251 | +#### 1. Verbosity |
| 252 | + |
| 253 | +This option sets the log verbosity level based on the [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424) syslog standard. It defines the severity of log messages that should be recorded. Available log levels are: |
| 254 | + |
| 255 | +- **emergency**: The highest severity level, indicating a system-wide emergency. |
| 256 | +- **alert**: Indicates an urgent condition that requires immediate attention. |
| 257 | +- **critical**: Highlights critical issues that need immediate action. |
| 258 | +- **error**: Used to log error messages that could affect functionality. |
| 259 | +- **warning**: Logs warnings about potential issues that should be monitored. |
| 260 | +- **notice**: Records significant but non-critical events. |
| 261 | +- **info**: Logs general informational messages indicating normal operation. |
| 262 | +- **debug**: The most detailed log level, useful for tracing and debugging the application. |
| 263 | + |
| 264 | +**Example:** |
| 265 | +Setting the log verbosity level to `error` ensures only error messages and above (i.e., critical, alert, emergency) will be captured: |
| 266 | + |
| 267 | +```json |
| 268 | +{ |
| 269 | + "logger": { |
| 270 | + "verbosity": "error" |
| 271 | + } |
| 272 | +} |
| 273 | +``` |
| 274 | + |
| 275 | +For a development environment, you might prefer a more verbose log level like `debug`: |
| 276 | + |
| 277 | +```json |
| 278 | +{ |
| 279 | + "logger": { |
| 280 | + "verbosity": "debug" |
| 281 | + } |
| 282 | +} |
| 283 | +``` |
| 284 | + |
| 285 | +#### 2. includeStackTrace |
| 286 | + |
| 287 | +This option specifies whether the stack trace should be included in the log messages. If set to `true`, stack traces will be included in error messages, which can be helpful during debugging. If set to `false`, logs will be more concise. |
| 288 | + |
| 289 | +```json |
| 290 | +{ |
| 291 | + "logger": { |
| 292 | + "includeStackTrace": true |
| 293 | + } |
| 294 | +} |
| 295 | +``` |
| 296 | + |
| 297 | +### Global configuration |
| 298 | + |
| 299 | +To configure the logger globally, add a `logger` object to the top-level configuration of the middleware. This configuration will apply to all integrations and parts of the application unless specifically overridden. Here’s an example of a global configuration where the stack trace is included in the logs, and the log level is set to `debug`: |
| 300 | + |
| 301 | +```js[middleware.config.js] |
| 302 | +export const config = { |
| 303 | + "logger": { |
| 304 | + "includeStackTrace": true, |
| 305 | + "verbosity": "debug" |
| 306 | + }, |
| 307 | + // The rest of the middleware configuration |
| 308 | + "integrations": { |
| 309 | + // ... |
| 310 | + } |
| 311 | +} |
| 312 | +``` |
| 313 | + |
| 314 | +This global logger configuration ensures that all logs, regardless of the integration or scope, will follow these settings. |
| 315 | + |
| 316 | +### Per integration configuration |
| 317 | + |
| 318 | +If you need different logging settings for specific integrations, you can configure the logger separately within each integration. The logger configuration should be added at the same level as `location` and `configuration` keys within the integration’s object. |
| 319 | + |
| 320 | +For example, here is how you would configure the logger for an integration with the key `commerce`: |
| 321 | + |
| 322 | +```js[middleware.config.js] |
| 323 | +export const config = { |
| 324 | + "integrations": { |
| 325 | + "commerce": { |
| 326 | + "location": "@vsf-enterprise/sapcc-api/server", |
| 327 | +
|
| 328 | + "configuration": { |
| 329 | + // Configuration of integration itself |
| 330 | + } |
| 331 | +
|
| 332 | + // Configuration of the logger only for "commerce" integration |
| 333 | + "logger": { |
| 334 | + "includeStackTrace": true, |
| 335 | + "verbosity": "debug" |
| 336 | + }, |
| 337 | + } |
| 338 | + } |
| 339 | +} |
| 340 | +``` |
| 341 | + |
| 342 | +In this case, the logging configuration applies only to the `commerce` integration, overriding the global settings if they exist. The logger configuration can vary for each integration, allowing you to customize the level of logging detail based on the needs of specific integrations. |
| 343 | + |
| 344 | +This approach provides the flexibility to define global logging behavior while also giving control to fine-tune logging per integration, ensuring you capture the necessary details for debugging and monitoring in the most relevant areas. |
0 commit comments