It goes without saying that logging plays a role in the development process. When there is a problem with the program, the first thing we want to do is trace it through logging monitoring.

Good logging can simulate the complete process of user operations as they use the application through the history that the application performs.

Want to know what happened

To help us figure out what’s wrong with the application, we’ll take the Logrock module and link it to ElasticSearch, LogStash, and Kibana for further analysis.

LogRock

The Logrock module grew out of an innovation when studying Cleverbrush. It is a software for processing vector graphics. Using a graphical editor means a large number of application use cases. To control costs, the entire development and testing process had to be optimized. Reduce the extra effort of using test cases at each stage.

This module can organize a modern logging approach for your application. Based on the log, we tested our application. In this article, I’ll show you how to organize your logging system to search for errors.

ElasticStack

  • ElasticSearch is a powerful full text search engine.
  • LogStash is a system for collecting logs from various sources that can also be sent to ElasticSearch.
  • Kibana is a Web interface version of ElasticSearch that contains many plugins.

How does it work

Once an error occurs in the program (or for a special simulation), the application sends the log to the server, and then saves the log to a file. Logstash Saves data increments to the ElasticSearch database. Users log in to Kibana and view the saved log.

This is a configured Kibana interface that displays data from ElasticSearch. It helps you analyze the data and learn from it what’s wrong with your program.

Here’s how to set up ElasticStack.

Creating a Log System

Here we integrate a logging system into a single page application developed based on React.

Step 1: Install

npm install logrock --save
Copy the code

Step 2: Integrate into the React application

import { LoggerContainer } from "logrock";

<LoggerContainer>
  <App />
</LoggerContainer>
Copy the code

LoggerContainer is a component that catches errors in your application and stacks them.

The stack is an object that contains information about the user’s operating system, browser, mouse or keyboard buttons pressed, and, of course, an action-related subarray that records all the actions performed by the user in the system.

LoggerContainer contains configuration items, and consider changing some of these Settings as appropriate.

<LoggerContainer
  active={true|false}
  limit={20}
  onError={stack => {
    sendToServer(stack);
  }}
>
  <App />
</LoggerContainer>
Copy the code
  • Active Enables or disables the log function
  • Limit Sets the maximum threshold for the most recent user action. If this value is exceeded, the first value in the array will be deleted. The array always holds information about the last 20 operations.
  • OnError A callback when the error starts. The stack object contains information about the environment and user actions. In the callback we need to upload the information to ElasticSearch, the cloud, or save it to a local file for later data analysis and monitoring.

Print log

In order to produce high quality user operation logs, we cover the log code in all places where we need to print logs

The Logrock module ships with a logger connected to the LoggerContainer.

For example, we have the following component:

import React, { useState } from "react";

export default function Toggle(props) {
  const [toggleState, setToggleState] = useState("off");

  function toggle() {
    setToggleState(toggleState === "off" ? "on" : "off");
  }

  return <div className={`switch ${toggleState}`} onClick={toggle} />;
}
Copy the code

To get the log to override correctly, we need to modify the toggle method:

import React, { useState } from "react";
import logger from "logrock";

export default function Toggle(props) {
  const [toggleState, setToggleState] = useState("off");

  function toggle() {
    let state = toggleState === "off" ? "on" : "off";

    logger.info(`React.Toggle|Toggle component changed state ${state}`);

    setToggleState(state);
  }

  return <div className={`switch ${toggleState}`} onClick={toggle} />;
}
Copy the code

We added a Logger method with a message in two parts. ‘React.Toggle’ is used to show that the action is taking place at the React Toggle component level. This is followed by some description of the action and the component in which it occurs. Logging level partitioning is not necessary, but it helps us quickly locate the code where the error occurred.

We can also catch exceptions using the “componentDidCatch” method introduced in React 16.

Upload the log

Suppose we have a way to collect user data from the back end. The method is asynchronous, with some logic hidden in the back end. How do you add logging to your code?

First, since we have a client application, all requests sent to the server will be passed within a user session without needing to reload the page. To associate an operation on the client with an operation on the server, we must create a global SessionID and add it to the tag for each request header for the server. On the server, we can use any logger to record our logic, as shown in the front end example. If an error occurs, send the data with the attached sessionID to ElasticSearch to send to the back end.

Step 1: generate an SessionID on the client

window.SESSION_ID = `sessionid-${Math.random().toString(36).substr(3, 9)}`;
Copy the code

Step 2: Encapsulate the request

We need to add the SessionID to the request header. If we use and encapsulate the request library, it is easy to add the declared SessionID to all requests.

let fetch = axios.create({... }); fetch.defaults.headers.common.sessionId = window.SESSION_ID;Copy the code

Step 3: bind the SessionID to the log stack

LoggerContainer has a special sessionID field

<LoggerContainer active={true | false} sessionID={window.SESSION_ID} limit={20} onError={stack => { sendToServer(stack);  }} > <App /> </LoggerContainer>Copy the code

Step 4: Request the back-end interface

The front-end request looks like the following:

logger.info(`store.getData|User is ready for loading... User ID is ${id}`);

getData('/api/v1/user', { id })
  .then(userData => {
    logger.info(`store.getData|User have already loaded. User count is ${JSON.stringify(userData)}`);
  })
  .catch(err => {
    logger.error(`store.getData|User loaded fail ${err.message}`);
  });
Copy the code

How does it work?

We write a log before the client requests it. From our code, we can see that we are now downloading data from the server. We have attached a SessionID to the request. If our backend log contains the addition of this SessionID and the request fails, then we can see what happens on the back end.

Therefore, we monitor the entire cycle of the application not only on the client but also on the server.

The user interaction

Some logs are helpful to users. The stDOUT method can be used to output the necessary information to the user

<LoggerContainer active={true | false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack);  }} stdout={(level, message, important) => { console[level](message); if (important) { alert(message); } }} > <App /> </LoggerContainer>Copy the code
  • The stdout method is used to print the prompt to the page

We declare that this is an important message that needs to be displayed in the Pop-up window. For example, if the data fails to load, we will output the following error message:

logger.log('Something was wrong', true);
Copy the code

Some considerations

  • Log monitoring (including production environments), because testers can never cover all test cases and fully simulate user operations
  • Don’t forget to mention log collection in your license agreement.
  • Do not record sensitive personal information such as passwords, bank details and addresses in the log.
  • Avoid adding redundant information to logs and keep them concise and useful

conclusion

When we finish coding and release the application, if the application is a life, then the life is just beginning. Collecting and monitoring logs can help you get feedback on your product to help you improve.