//App.jsx
import React, { Component } from 'react';
import { initialiseLogger } from './logger';
class App extends Component {
constructor(props) {
super(props);
initialiseLogger(props.environment);
}
render() {
return (
<Component />
);
}
}
export default App;
This it he top level of my React app. I initialise my logger here. And below is the logger. I am using our ui logger by my project. It exposes one function called configure that creates a logger based on some params. Each time it gets called it will create a new logger.
//logger.js
import LoggerFactory from '@company/ui-logger';
import appConfigurations from '../shared/appConfigurations';
const loggerContainer = {};
const initialiseLogger = (env) => {
const url = appConfigurations.getLogUrl(env);
const service = appConfigurations.getLogUrl(env);
const version = appConfigurations.getComponentVersion(env);
const apigeeClientId = appConfigurations.getComponentName(env);
Object.freeze(
Object.assign(
loggerContainer,
{ logger: LoggerFactory.configure({ url, service, version, apigeeClientId }) }
)
);
return loggerContainer.logger;
};
const getLogger = () => {
if (!loggerContainer.logger) {
// eslint-disable-next-line no-console
console.error('A logger has not been initialised');
}
return loggerContainer.logger;
};
export { initialiseLogger, getLogger };
As you can see initialiseLogger gets some app config and creates a logger which then gets assigned inside a const object that then becomes immutable. I also expose a getLogger method that is going to be used in the other components to get the logger and then log what they want.
Example:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { getLogger } from '../logger';
class ErrorBox extends Component {
constructor() {
super();
this.state = {
isVisible: false,
apiErrors: [],
uiErrors: []
};
this.logger = getLogger();
}
componentDidMount() {
...
}
componentWillUnmount() {
...
}
updateErrorList = () => {
...
}
componentDidCatch(error) {
this.setState({
isVisible: true,
uiErrors: [error]
});
this.logger.error(
{ message: error.message },
error.stack
);
}
render() {
...
}
}
export default ErrorBox;
I am not feeling 100% sure about this code. Should I go for a singleton pattern and just expose the getLogger function.
Something like
import LoggerFactory from '@company/ui-logger';
import appConfigurations from '../shared/appConfigurations';
const loggerContainer = {};
const initialiseLogger = (env) => {
const url = appConfigurations.getLogUrl(env);
const service = appConfigurations.getLogUrl(env);
const version = appConfigurations.getComponentVersion(env);
const apigeeClientId = appConfigurations.getComponentName(env);
Object.freeze(
Object.assign(
loggerContainer,
{ logger: LoggerFactory.configure({ url, service, version, apigeeClientId }) }
)
);
return loggerContainer.logger;
};
const getLogger = () => {
if (!loggerContainer.logger) {
initialiseLogger('env');
}
return loggerContainer.logger;
};
export default getLogger;
Or even
import LoggerFactory from '@company/ui-logger';
import appConfigurations from '../shared/appConfigurations';
const Logger = () => {
let instance;
const initialiseLogger = (env) => {
const url = appConfigurations.getLogUrl(env);
const service = appConfigurations.getLogUrl(env);
const version = appConfigurations.getComponentVersion(env);
const apigeeClientId = appConfigurations.getComponentName(env);
return LoggerFactory.configure({ url, service, version, apigeeClientId });
};
return {
getLogger() {
if (!instance) {
instance = initialiseLogger('env');
}
return instance;
}
};
};
export default Logger();
But i am not sure about my design of this singleton. Would the instance be visble to getLogger when I export it as a module to another compoent?