@s-ui/logger v1.6.0
@s-ui/logger
Web app logging tools for both, client and server side.
Table of contents
- @s-ui/logger
Installation
npm install @s-ui/logger
Application logging
Client side logging with reporter
To start logging client-side logs in our application, we should initialize our tracker.
// app.js
import {Reporter} from '@adv-ui/reporter' // Service Logger
import {initTracker} from '@s-ui/logger'
initTracker({Reporter, appName: 'milanuncios', environment: 'production', devMode: false})Options:
Reporter{Function} - Application LoggerappName{String} - Application namedevMode{String} - Allows sending events to the development endpoint, Production endpoint is used by default.environment{String} - Optionally set the environment property to be used as part of the context (e.g.preproductionorproduction). If not setNODE_ENVwill be used as default value...rest- See your Service Logger client configuration
After initializing our tracker, we could create our logger
import {createClientLogger} from '@s-ui/logger'
export const getLogger = ({isClient, userId}) => {
const logger = isClient ? createClientLogger({userId}) : {} // see next section
return {logger}
}Options:
userId{String} - User id to add to all logstrackerName{String} optional - Tracker name that will be used by the microserviceš” Please note that, if you decide to use a custom trackerName, you'd need to create a custom provider inside your service.
Server side logging
We could start logging in our app in two different ways.
With Stdout
This logging system is maintained by es-common-platform and get logs automatically from stdout.
This server side logging keeps you log whatever with native console methods. Also, it enables two ways of logging.
- Patch consolelog|info|error|warn in order to re-send console with correct format. You could use native console methods.
createServerLoggerreturns a logger withlog|info|error|warnmethods in order to be used in our context application. It could be redundant if we like use native console methods.
Also, it will capture unhandled errors and send them as console.error with appropriated format.
import {createServerLogger} from '@s-ui/logger/lib/server/logger'
export const getLogger = ({req}) => {
return createServerLogger({req, team: 'frontend-ma'})
}createServerLogger accepts those parameters:
req: server request objectgetTenantService: Function to parse request and returns an string with tenant value and add it to logsuserId: user idteam: should match with deploy tags.yml team field.
Further information
Enabled by default, there is a ENV var to disable it DISABLE_SERVER_LOGGER_PATCH=true. This help us to disable without compile app again.
With Reporter
The Reporter needs a
startmethod to inicialize the reporter.
export const initTracker = ({Reporter, appName, environment, version, tenant, ...config}) => {
const context = {environment: environment || NODE_ENV, isServer, version, tenant}
Reporter.start(appName, {
...config,
context
})
}To start logging server-side logs in our application, we should initialize our tracker in one server file
// app.js
import {Reporter} from '@adv-ui/reporter' // Service Logger
import {initTracker} from '@s-ui/logger'
initTracker({Reporter, appName: 'milanuncios', devMode: false})Options:
Reporter{Function} - Service LoggerappName{String} - Application namedevMode{String} - Allows sending events to the development endpoint, Production endpoint is used by default....rest- See your Service Logger client configuration
After initializing our tracker, we could create our logger.
import {createServerLogger} from '@s-ui/logger'
export const getLogger = ({isClient, userId}) => {
const logger = isClient
? {} // see previous section
: createServerLogger({userId})
return {logger}
}Options:
userId{String} - User id to add to all logs
How to consume Reporter logs
When we create a reporter logger function, we get a logger with three methods: error, log and metric.
Error method
It accepts one parameter with message, name, stack properties (usually an Error).
logger.error(new Error('Something went wrong'))We could consume in ELK:
reporter.errorMessage: {string}reporter.errorName: {string}reporter.errorStack: {string}
Patch native console.error
It behaves similar to logger.error.
console.error(new Error('Something went wrong'))We could consume in ELK:
reporter.errorMessage: {string}reporter.errorName: {string}reporter.errorStack: {string}
Log method
It accepts a message string parameter.
logger.log('Something happened')We could consume in DataDog
Performance method
This method allows us to record performance metrics, more specifically Core Web Vitals metrics.
In addition to recording the value of the metric, we can record: the path, the element that has triggered or affects the metric, and the status of the load.
| Property | Description |
|---|---|
| name | Core Web Vital metric name |
| amount | The metric value |
| path | Application route |
| target | Element that has triggered or affects the metric |
| loadState | The loading state of the document |
Example with the INP metric
const inpMetrics = {
name: 'cwv.inp', // Core Web Vital metric name
amount: '872', //
path: '/:lang',
target: 'div.event-target',
loadState: 'dom-content-loaded'
}
logger.cwv({...inpMetrics})We could consume in Open Search to debug Web Performance Issues.
Metric method
It accepts a Metric object containing a name and tags
š” Please note that in order for metrics to work, you'd need to create a custom provider inside the ms-adit--reporter microservice.
logger.metric({
name: 'some_metric_name', // š” It is highly recommended to use snake_case
// Anything you wish to tag
tags: [
{key: 'isCondition', value: 'yes | no'},
{key: 'result', value: 'ok'}
]
})We can then filter in Datadog with the count of the occurrences for this metric.
Timing method
It accepts a Timing object containing a name, amount and tags
š” Please note that similarly to previous method in order to make it work, you'd need to create a custom provider inside the ms-adit--reporter microservice.
logger.timing({
name: 'some_metric_name', // š” It is highly recommended to use snake_case
amount: 156.43, // Time in milliseconds
// Anything you wish to tag
tags: [
{key: 'isCondition', value: 'yes | no'},
{key: 'result', value: 'ok'}
]
})Trace method
It accepts a name, a function and optionally an options object. The method returns the same function that was provided but wrapped to send performance timing metrics out of the box using the timing method.
export default class GetDiscardedListUseCase {
constructor({repository, logger}) {
this.repository = repository
this._logger = logger
this.execute = this._logger.trace('GetDiscardedListUseCase#execute', this.execute)
}
async execute = ({sessionId, userId, locale}) => {
const discardedList = await this.repository
.user({id: userId})
.session({sessionId})
.getDiscardedList({locale})
return discardedList.toJSON()
}
}The options object can optionally contain:
A
tagsfield. The tags are a set of properties that will be send in the timing eventsA
logErrorsfield. This enables error-logging upon an error occurring
logger.trace('name', () => {}, {
tags: [{key: 'path', value: '/'}],
logErrors: true
})- An
onSuccesscallback that can add additional tags when the use case finishes. It will be called with the response of the use case and you can add any other tags that you need.
logger.trace('name', () => {}, {
tags: [{key: 'path', value: '/'}],
onSuccess: response => {
if (response.isProUser === true) {
return [
{
key: 'type',
value: 'professional'
}
]
}
return []
}
})- An
onErrorcallback that can add additional tags when the use case fails. It will be called with the error of the use case and you can add any other tags that you need.
logger.trace('name', () => {}, {
tags: [{key: 'path', value: '/'}],
onError: error => {
if (error.message === 'MISSING_INFO') {
return [
{
key: 'reason',
value: 'missing_info'
}
]
}
return []
}
})- A
filtercallback that can be used to avoid sending metrics when the use case fails.- When
trueis returned the metrics and logs are not sent - When
falseis returned the metrics and logs are sent
- When
logger.trace('name', () => {}, {
tags: [{key: 'path', value: '/'}],
filter: error => {
return error.message === 'EXPIRED_TOKEN'
}
})Server logging
Express middleware for sui-ssr logging hook
@s-ui/ssr accepts hooks (express middlewares), one of them is for logging and we could add here our logging hook with needed options.
Example file: ./src/hooks/index.js
import TYPES from '@s-ui/ssr/hooks-types'
import {getExpressMiddleware} from '@s-ui/logger/lib/server/expressMiddleware'
import routes from '../routes'
const getTenantService = req => {
const TENANT_COCHES = 'coches'
const TENANT_MOTOS = 'motos'
return req.headers.host.includes(TENANT_MOTOS) ? TENANT_MOTOS : TENANT_COCHES
}
const loggingMiddleware = getExpressMiddleware({
appName: 'frontend-mt--web-app',
dataDogOptions: {globalTags: {node_ssr: 'motor'}, routes},
stdoutOptions: {
getTenantService,
team: 'frontend-mt'
}
})
export default () => {
try {
return {
[TYPES.LOGGING]: loggingMiddleware
}
} catch (err) {
console.error('[hooks] Something was really wrong', err.msg) // eslint-disable-line
return {}
}
}Then, use can configure this hook following @s-ui/ssr instructions
StdoutLogger
Those logs will be sent to our ELK Service.
Options:
getTenantService{Function} - It receives the request as an argument and should return a string with matching tenantteam{String} - It indicates the owner of that service. It is required, logs that does not have team property set will be ignored
DataDogLogger
Those logs will be sent to Datadog, using hot-shots library.
Options:
globalTags{Object} - This properties will be used inglobalTagsparam onhot-shotsclient creation. It should have at least this attribute{node_ssr: 'app_name'}routes{import('react').ComponentType} - React routes
Express middleware for sui-ssr logging hook using Reporter
@s-ui/ssr accepts hooks (express middlewares), one of them is for logging and we could add here our logging hook.
import TYPES from '@s-ui/ssr/hooks-types'
import {logErrorsMiddleware} from '@s-ui/logger'
export default () => {
try {
return {
[TYPES.LOGGING]: logErrorsMiddleware
}
} catch (err) {
console.error('[hooks] Something was really wrong', err.msg) // eslint-disable-line
return {}
}
}Tracking page fetching
Use traceInitialProps to keep track of a page fetching function using the timing method from the logger. If the logger is not defined inside the context of the application it will do nothing.
import {traceInitialProps} from '@s-ui/logger'
function HomePage() {}
HomePage.displayName = 'HomePage'
HomePage.getInitialProps = traceInitialProps(({req, context, routeInfo}) => {
// do something
return {}
})
export default HomePageConsume Stdout logs
š Check your Logger Service
Available fields:
http_status_code: for all the server requests ifgetExpressMiddlewarehas been implemented.message: for logged strings or errorserror ({message, stack}): for caught exceptions or logged errors- Also, accept custom fields. Just make a
console[method]with one object parameter. For example:console.log({message: 'my custom log', data: {value: 'sth I want to read'}}). Be careful using large objects, we recomend{message, data}format.
11 months ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago