Browse Source

Added good logging to client

update-deps
Alex Mikhalev 7 years ago
parent
commit
50e9583e0f
  1. 10
      .vscode/tasks.json
  2. 2
      app/index.tsx
  3. 136
      common/logger.ts
  4. 29
      common/mqtt.ts

10
.vscode/tasks.json vendored

@ -23,6 +23,16 @@
} }
] ]
} }
},
{
"type": "npm",
"script": "watch:server",
"problemMatcher": []
},
{
"type": "npm",
"script": "start:pretty",
"problemMatcher": []
} }
] ]
} }

2
app/index.tsx

@ -4,7 +4,7 @@ import { AppContainer } from "react-hot-loader";
import App from "@app/components/App"; import App from "@app/components/App";
import { ProvideState, State } from "@app/state"; import { ProvideState, State } from "@app/state";
import log, { setLogger } from "@common/log"; import log, { setLogger } from "@common/logger";
setLogger(log.child({ name: "sprinklers3/app" })); setLogger(log.child({ name: "sprinklers3/app" }));

136
common/logger.ts

@ -1,6 +1,140 @@
import * as pino from "pino"; import * as pino from "pino";
let logger: pino.Logger = pino(); type Level = "default" | 60 | 50 | 40 | 30 | 20 | 10;
const levels = {
default: "USERLVL",
60: "FATAL",
50: "ERROR",
40: "WARN",
30: "INFO",
20: "DEBUG",
10: "TRACE",
};
const levelColors = {
default: "text-decoration: underline; color: #000000;",
60: "text-decoration: underline; background-color: #FF0000;",
50: "text-decoration: underline; color: #FF0000;",
40: "text-decoration: underline; color: #FFFF00;",
30: "text-decoration: underline; color: #00FF00;",
20: "text-decoration: underline; color: #0000FF;",
10: "text-decoration: underline; color: #AAAAAA;",
};
interface ColoredString {
str: string;
args: any[];
}
function makeColored(str: string = ""): ColoredString {
return { str, args: [] };
}
function concatColored(...coloredStrings: ColoredString[]): ColoredString {
return coloredStrings.reduce((prev, cur) => ({
str: prev.str + cur.str,
args: prev.args.concat(cur.args),
}), makeColored());
}
const standardKeys = ["pid", "hostname", "name", "level", "time", "v", "source", "msg"];
function formatter(value: any) {
let line = concatColored(
// makeColored(formatTime(value, " ")),
formatSource(value),
formatLevel(value),
makeColored(": "),
);
if (value.msg) {
line = concatColored(line, {
str: "%c" + value.msg, args: ["color: #00FFFF"],
});
}
let args = [line.str].concat(line.args);
if (value.type === "Error") {
args = args.concat([value.stack]);
} else {
args = args.concat([filter(value)]);
}
let fn;
if (value.level >= 50) {
fn = console.error;
} else if (value.level >= 40) {
fn = console.warn;
} else {
fn = console.log;
}
fn.apply(null, args);
}
function withSpaces(value: string): string {
const lines = value.split("\n");
for (let i = 1; i < lines.length; i++) {
lines[i] = " " + lines[i];
}
return lines.join("\n");
}
function filter(value: any) {
const keys = Object.keys(value);
const result: any = {};
for (const key of keys) {
if (standardKeys.indexOf(key) < 0) {
result[key] = value[key];
}
}
return result;
}
function asISODate(time: string) {
return new Date(time).toISOString();
}
function formatTime(value: any, after?: string) {
after = after || "";
try {
if (!value || !value.time) {
return "";
} else {
return "[" + asISODate(value.time) + "]" + after;
}
} catch (_) {
return "";
}
}
function formatSource(value: any): { str: string, args: any[] } {
if (value.source) {
return { str: "%c(" + value.source + ") ", args: ["color: #FF00FF"] };
} else {
return { str: "", args: [] };
}
}
function formatLevel(value: any): ColoredString {
const level = value.level as Level;
if (levelColors.hasOwnProperty(level as string)) {
return {
str: "%c" + levels[level] + "%c",
args: [levelColors[level], ""],
};
} else {
return {
str: levels.default,
args: [levelColors.default],
};
}
}
let logger: pino.Logger = pino({
browser: { write: formatter },
level: "trace",
});
export function setLogger(newLogger: pino.Logger) { export function setLogger(newLogger: pino.Logger) {
logger = newLogger; logger = newLogger;

29
common/mqtt.ts

@ -1,5 +1,6 @@
import * as mqtt from "../node_modules/mqtt"; import * as mqtt from "../node_modules/mqtt";
import logger from "./logger";
import { import {
Duration, Duration,
ISprinklersApi, ISprinklersApi,
@ -14,6 +15,8 @@ import {
} from "./sprinklers"; } from "./sprinklers";
import { checkedIndexOf } from "./utils"; import { checkedIndexOf } from "./utils";
const log = logger.child({ source: "mqtt" });
export class MqttApiClient implements ISprinklersApi { export class MqttApiClient implements ISprinklersApi {
readonly mqttUri: string; readonly mqttUri: string;
client: mqtt.Client; client: mqtt.Client;
@ -30,7 +33,7 @@ export class MqttApiClient implements ISprinklersApi {
start() { start() {
const clientId = MqttApiClient.newClientId(); const clientId = MqttApiClient.newClientId();
console.log("connecting to mqtt with client id %s", clientId); log.info({ clientId }, "connecting to mqtt with client id");
this.client = mqtt.connect(this.mqttUri, { this.client = mqtt.connect(this.mqttUri, {
clientId, clientId,
}); });
@ -38,11 +41,11 @@ export class MqttApiClient implements ISprinklersApi {
this.client.on("offline", () => { this.client.on("offline", () => {
this.connected = false; this.connected = false;
}); });
this.client.on("error", (e) => { this.client.on("error", (err) => {
console.error("mqtt error: ", e); log.error({ err }, "mqtt error");
}); });
this.client.on("connect", () => { this.client.on("connect", () => {
console.log("mqtt connected"); log.info("mqtt connected");
this.connected = true; this.connected = true;
for (const prefix of Object.keys(this.devices)) { for (const prefix of Object.keys(this.devices)) {
const device = this.devices[prefix]; const device = this.devices[prefix];
@ -76,19 +79,19 @@ export class MqttApiClient implements ISprinklersApi {
private onMessageArrived(topic: string, payload: Buffer, packet: mqtt.Packet) { private onMessageArrived(topic: string, payload: Buffer, packet: mqtt.Packet) {
try { try {
this.processMessage(topic, payload, packet); this.processMessage(topic, payload, packet);
} catch (e) { } catch (err) {
console.error("error while processing mqtt message", e); log.error({ err }, "error while processing mqtt message");
} }
} }
private processMessage(topic: string, payload: Buffer, packet: mqtt.Packet) { private processMessage(topic: string, payload: Buffer, packet: mqtt.Packet) {
console.log("message arrived: ", { topic, payload }); log.trace({ topic, payload }, "message arrived: ");
const topicIdx = topic.indexOf("/"); // find the first / const topicIdx = topic.indexOf("/"); // find the first /
const prefix = topic.substr(0, topicIdx); // assume prefix does not contain a / const prefix = topic.substr(0, topicIdx); // assume prefix does not contain a /
const topicSuffix = topic.substr(topicIdx + 1); const topicSuffix = topic.substr(topicIdx + 1);
const device = this.devices[prefix]; const device = this.devices[prefix];
if (!device) { if (!device) {
console.warn(`received message for unknown device. prefix: ${prefix}`); log.debug({ prefix }, "received message for unknown device");
return; return;
} }
device.onMessage(topicSuffix, payload); device.onMessage(topicSuffix, payload);
@ -143,7 +146,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
const payload = payloadBuf.toString("utf8"); const payload = payloadBuf.toString("utf8");
if (topic === "connected") { if (topic === "connected") {
this.connected = (payload === "true"); this.connected = (payload === "true");
// console.log(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`) log.trace(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`);
return; return;
} }
let matches = topic.match(/^sections(?:\/(\d+)(?:\/?(.+))?)?$/); let matches = topic.match(/^sections(?:\/(\d+)(?:\/?(.+))?)?$/);
@ -151,7 +154,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
/* tslint:disable-next-line:no-unused-variable */ /* tslint:disable-next-line:no-unused-variable */
const [_topic, secStr, subTopic] = matches; const [_topic, secStr, subTopic] = matches;
// console.log(`section: ${secStr}, topic: ${subTopic}, payload: ${payload}`); log.trace({ section: secStr, topic: subTopic, payload });
if (!secStr) { // new number of sections if (!secStr) { // new number of sections
this.sections.length = Number(payload); this.sections.length = Number(payload);
} else { } else {
@ -169,7 +172,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
/* tslint:disable-next-line:no-unused-variable */ /* tslint:disable-next-line:no-unused-variable */
const [_topic, progStr, subTopic] = matches; const [_topic, progStr, subTopic] = matches;
// console.log(`program: ${progStr}, topic: ${subTopic}, payload: ${payload}`); log.trace({ program: progStr, topic: subTopic, payload });
if (!progStr) { // new number of programs if (!progStr) { // new number of programs
this.programs.length = Number(payload); this.programs.length = Number(payload);
} else { } else {
@ -192,7 +195,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
/* tslint:disable-next-line:no-unused-variable */ /* tslint:disable-next-line:no-unused-variable */
const [_topic, respIdStr] = matches; const [_topic, respIdStr] = matches;
// console.log(`response: ${respIdStr}`); log.trace({ response: respIdStr });
const respId = parseInt(respIdStr, 10); const respId = parseInt(respIdStr, 10);
const data = JSON.parse(payload) as IResponseData; const data = JSON.parse(payload) as IResponseData;
const cb = this.responseCallbacks[respId]; const cb = this.responseCallbacks[respId];
@ -201,7 +204,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
} }
return; return;
} }
console.warn(`MqttSprinklersDevice recieved invalid topic: ${topic}`); log.warn({ topic }, "MqttSprinklersDevice recieved invalid message");
} }
runSection(section: Section | number, duration: Duration) { runSection(section: Section | number, duration: Duration) {

Loading…
Cancel
Save