Skip to content

A generic server that starts, logs via POST into database, can process and serve logs, and can be stopped

License

Notifications You must be signed in to change notification settings

GonchuB/log-server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

log-server

A generic server that starts, logs via POST into database, can process and serve logs, and can be stopped

Problem

Gathering data from a running application or script and processing the outcome afterwards.

Solution

Use LokiJS as an in-memory document database to store log entries. Create a REST api that allows resetting the entries, adding a new entry and processing the entries. Make the server generic, allowing for entry points to configure how the data is stored in the database and how the result is produced.

Installation

yarn

or

npm install

Usage

1. Starting the server

With key / value mocks

node index.js

With implementation

node index.js -f ./path/to/implementation.js

2. Logging

  1. First start the service (automatically starts when running) to clear existing entries.
curl -X POST \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
'http://127.0.0.1:3000/log-start'
  1. Log your entries
curl -X POST \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
-d '{"component":"MarkdownComponent", "page": "Welcome", "time": 1000}' \
'http://127.0.0.1:3000/log'
  1. Produce final report
curl -X POST \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
'http://127.0.0.1:3000/log-end'

3. Custom implementations

Custom implementations need to define 3 functions:

  • key: (requestBody: object) => string;
  • value: (requestBody: object) => string | object;
  • produce: (cacheObject: object) => string | object;

Example

function key(body) {
  const { component, page } = body;
  return [page, component].join(".");
}

function value(body) {
  const { time } = body;
  return time;
}

function produce(cache) {
  return Object.entries(cache).reduce(reducePages, {});
}

function reducePages(accum, [page, componentsObj]) {
  return {
    ...accum,
    [page]: Object.entries(componentsObj).reduce(reduceComponents, {})
  };
}

function reduceComponents(componentsAccum, [component, times]) {
  return {
    ...componentsAccum,
    [component]: {
      avg: times.reduce((a, b) => a + b, 0) / times.length,
      _count: times.length
    }
  };
}

module.exports = { key, value, produce };

NOTE: the cacheObject in the produce function is an object whose shape will be determined by the entries' keys (see entriesToObject)

// entriesToObject example

const entries = [
  { key: "a.b", value: 1 },
  { key: "a.b", value: 2 },
  { key: "a.c", value: 3 }
];

// groupBy(entries, "key") + 🍫

return { a: { b: [1, 2], c: [3] } }; // cacheObject

Inspiration

How to use React's experimental new profiler feature

Using profile implementation.

// withAsyncBenchmark.js
import React from "react";

function withAsyncBenchmark(WrappedComponent, id = "BenchmarkComponent") {
  const Profiler = React.unstable_Profiler;

  const LOG_SERVER_URL = "https://127.0.0.1:3000/log";
  const LOG_SERVER_METHOD = "POST";
  const LOG_SERVER_HEADERS = {
    "Content-Type": "application/json",
    Accept: "application/json"
  };

  function onRender(component, mode, actualTime) {
    fetch(LOG_SERVER_URL, {
      body: JSON.stringify({ component, mode, value: actualTime }),
      method: LOG_SERVER_METHOD,
      headers: LOG_SERVER_HEADERS
    });
  }

  return function WithProfiler(props) {
    return (
      <Profiler id={id} onRender={onRender}>
        <WrappedComponent {...props} />
      </Profiler>
    );
  };
}
// ./Page.js
import withAsyncBenchmark from "./withAsyncBenchmark";
import ComponentToBenchmark from "./Page.component";

export default withAsyncBenchmark(ComponentToBenchmark, "Page");
// Sample payload send to log-server via `onRender` function
{ component: 'Page', mode: 'mount', value: 12.210000189952552 }
// Sample aggregation of entries
{
  Page: {
    mount: {
      avg: 0.1593013390228786,
      max: 1.4900000533089042,
      min: 0.054999953135848045,
      _count: 823
    }
  }
}

License

MIT Licence (c) Gonzalo Beviglia

About

A generic server that starts, logs via POST into database, can process and serve logs, and can be stopped

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •