|
| 1 | +# Sofie: The Modern TV News Studio Automation System (Logging library) |
| 2 | + |
| 3 | +[](https://www.npmjs.com/package/@sofie-automation/logging) |
| 4 | + |
| 5 | +This is a part of the [**Sofie** TV News Studio Automation System](https://github.com/Sofie-Automation/Sofie-TV-automation/). |
| 6 | + |
| 7 | +## What is it? |
| 8 | + |
| 9 | +This is a thin wrapper around the LogTape library (https://logtape.org) with some Sofie-recommended defaults and restrictions. |
| 10 | + |
| 11 | +The reason for using this package is: |
| 12 | + |
| 13 | +- To have a unified logging setup across the Sofie Automation projects |
| 14 | +- To ensure logs are easily parsable by log collectors such as Elasticsearch by avoiding logging arbitrary data structures. |
| 15 | + |
| 16 | +## Usage |
| 17 | + |
| 18 | +_Note: The official LogTape documentation applies to this as well, see https://logtape.org_ |
| 19 | + |
| 20 | +### In application: Configuration |
| 21 | + |
| 22 | +#### Configure and initialize logging at startup |
| 23 | + |
| 24 | +_It is mandatory that the application sets the configuration upon standup_ |
| 25 | + |
| 26 | +```typescript |
| 27 | +import { loggingDefaultConfigure, getDefaultConfiguration, loggingConfigure } from '@sofie-automation/logging' |
| 28 | + |
| 29 | +await loggingDefaultConfigure() // Sets up logging with Sofie-recommended defaults |
| 30 | + |
| 31 | +// or: |
| 32 | + |
| 33 | +await loggingDefaultConfigure({ |
| 34 | + logLevel: 'debug', // Set a log level |
| 35 | + logPath: '/var/logs/my-application', // Enable logging to disk |
| 36 | +}) |
| 37 | + |
| 38 | +// or: |
| 39 | + |
| 40 | +// This is equivalent to calling loggingDefaultConfigure(): |
| 41 | +const config = getDefaultConfiguration() |
| 42 | +await loggingConfigure(config) |
| 43 | +``` |
| 44 | + |
| 45 | +#### Reconfigure logging at runtime |
| 46 | + |
| 47 | +```typescript |
| 48 | +import { loggingDefaultConfigure } from '@sofie-automation/logging' |
| 49 | + |
| 50 | +// Example: Change the log level: |
| 51 | +await loggingDefaultConfigure({ |
| 52 | + reset: true, // Reset previous configuration |
| 53 | + logLevel: 'debug', // Set a log level |
| 54 | +}) |
| 55 | + |
| 56 | +// Note: If you have a custom logging configuration, you have to instead modify modify it and pass it into loggingConfigure() again. |
| 57 | +``` |
| 58 | + |
| 59 | +### Logging |
| 60 | + |
| 61 | +```typescript |
| 62 | +import { getLogger, SofieLogger } from '@sofie-automation/logging' |
| 63 | + |
| 64 | +class MyLibraryClass { |
| 65 | + logger: SofieLogger |
| 66 | + constructor() { |
| 67 | + // Setup a logger for this class. |
| 68 | + // Set the category so that the origin of the logs can be identified: |
| 69 | + this.logger = getLogger(['my-library', 'MyLibraryClass']) |
| 70 | + } |
| 71 | + logAMessage() { |
| 72 | + logger.info('Hello world!') |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +It is possible to provide the logger with additional context data. |
| 78 | +By default, only the `data` field is allowed: |
| 79 | + |
| 80 | +```typescript |
| 81 | +const logger = getLogger(['my-test-category']) |
| 82 | +logger.info('got some data', { data: myDataObject }) // The `data` field is always converted to JSON-string before logged. |
| 83 | +``` |
| 84 | + |
| 85 | +If you want to log some custom data fields, you need to extend the SofieLogger type to allow those fields: |
| 86 | + |
| 87 | +```typescript |
| 88 | +declare module '@sofie-automation/logging' { |
| 89 | + interface SofieLoggerContext { |
| 90 | + userId?: string |
| 91 | + sessionId?: string |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +const logger = getLogger(['my-test-category']) |
| 96 | +logger.info('user logged in', { |
| 97 | + // These fields are now allowed: |
| 98 | + userId: 'user-1234', |
| 99 | + session: 'session-5678', |
| 100 | +}) |
| 101 | +``` |
| 102 | + |
| 103 | +The reason for not allowing any data in the context (as is the LogTape way), |
| 104 | +it turns out that logging arbitrary data structures can lead to issues with log collectors, |
| 105 | +as they may not be able to parse and index all data structures correctly. |
| 106 | + |
| 107 | +#### Inheritance and contextual loggers |
| 108 | + |
| 109 | +```typescript |
| 110 | +import { getLogger } from '@sofie-automation/logging' |
| 111 | +declare module '@sofie-automation/logging' { |
| 112 | + interface SofieLoggerContext { |
| 113 | + userId?: string |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +const parentLogger = getLogger('my-library') |
| 118 | + |
| 119 | +// Example of creating a child logger with a sub-category: |
| 120 | +const childLogger = parentLogger.getChild('child-category') |
| 121 | +childLogger.info('This is from the child logger') // outputs category "my-library.child-category" |
| 122 | + |
| 123 | +// Example of creating a contextual logger: |
| 124 | +const withContextLogger = parentLogger.with({ userId: 'user-1234' }) |
| 125 | +withContextLogger.info('User did something') // outputs with userId in the log context |
| 126 | +``` |
| 127 | + |
| 128 | +#### Advanced: Custom filters |
| 129 | + |
| 130 | +An application may implement custom log filters, for example to omit logs from certain categories. |
| 131 | + |
| 132 | +```typescript |
| 133 | +import { getLogger, getDefaultConfiguration, loggingConfigure } from '@sofie-automation/logging' |
| 134 | + |
| 135 | +const config = getDefaultConfiguration({ |
| 136 | + reset: true, |
| 137 | + logLevel: 'debug', // debug and above |
| 138 | +}) |
| 139 | + |
| 140 | +// Add a filter: |
| 141 | +const filters = (config.filters = config.filters ?? {}) |
| 142 | +filters.myCustomFilter = (log) => { |
| 143 | + if (log.category.includes('special-category')) return false // Don't log this |
| 144 | + return true |
| 145 | +} |
| 146 | + |
| 147 | +// Assign the filter to all loggers: |
| 148 | +config.loggers.forEach((logger) => (logger.filters = [...(logger.filters || []), 'myCustomFilter'])) |
| 149 | + |
| 150 | +await loggingConfigure(config) |
| 151 | + |
| 152 | +const logger = getLogger('my-category') |
| 153 | +const specialLogger = logger.getChild('special-category') |
| 154 | + |
| 155 | +logger.info('This message will appear') |
| 156 | +specialLogger.info('This message will NOT appear') |
| 157 | +``` |
0 commit comments