mirror of
https://github.com/sensebox/blockly-app
synced 2025-06-08 10:05:50 +02:00
add LoggingProvider
unified interface for console & remote JSON logging! had to write my own as i don't want to send this information through google. fixup! add LoggingProvider
This commit is contained in:
parent
816d672383
commit
8010bd0a0c
8 changed files with 233 additions and 14 deletions
|
@ -11,7 +11,7 @@ It is based on Google's [Blockly](https://developers.google.com/blockly/) and Ca
|
|||
|
||||
## Development
|
||||
|
||||
This is an Ionic 2 / Angular 5 application using Cordova Plugins for mobile-native functionality.
|
||||
This is an Ionic 3 / Angular 5 application using Cordova Plugins for mobile-native functionality.
|
||||
|
||||
### dev env setup
|
||||
For a basic web version, only Node.js 8+ is required.
|
||||
|
|
|
@ -86,4 +86,5 @@
|
|||
<plugin name="cordova-plugin-network-information" spec="2.0.1" />
|
||||
<engine name="android" spec="7.1.1" />
|
||||
<engine name="browser" spec="5.0.4" />
|
||||
<plugin name="cordova-plugin-app-version" spec="0.1.9" />
|
||||
</widget>
|
||||
|
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "openSenseApp",
|
||||
"version": "0.0.1",
|
||||
"name": "blockly-sensebox",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -91,6 +91,14 @@
|
|||
"tslib": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"@ionic-native/app-version": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/app-version/-/app-version-5.1.0.tgz",
|
||||
"integrity": "sha512-u+j319sZBBGjf0MjeMhxKpT+iwL8R6bq4MBAbQOV+x/pyGuqed0RvoEZ5TO/XEpBLYrizkiKGffaNqlxHm1W+A==",
|
||||
"requires": {
|
||||
"@types/cordova": "^0.0.34"
|
||||
}
|
||||
},
|
||||
"@ionic-native/core": {
|
||||
"version": "4.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-4.15.0.tgz",
|
||||
|
@ -1060,6 +1068,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-2.0.1.tgz",
|
||||
"integrity": "sha1-qmd4jmS/qGUmkad7Ais7QDEgkRM="
|
||||
},
|
||||
"@types/cordova": {
|
||||
"version": "0.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
|
||||
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ="
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
@ -2830,6 +2843,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"cordova-plugin-app-version": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-app-version/-/cordova-plugin-app-version-0.1.9.tgz",
|
||||
"integrity": "sha1-nbBgeGMzenEEiTAuX1CpBPFEm9s="
|
||||
},
|
||||
"cordova-plugin-device": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-device/-/cordova-plugin-device-2.0.2.tgz",
|
||||
|
@ -3663,8 +3681,7 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -4029,8 +4046,7 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -4078,7 +4094,6 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -4117,13 +4132,11 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8003,6 +8016,7 @@
|
|||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"@angular/http": "5.2.11",
|
||||
"@angular/platform-browser": "5.2.11",
|
||||
"@angular/platform-browser-dynamic": "5.2.11",
|
||||
"@ionic-native/app-version": "^5.1.0",
|
||||
"@ionic-native/core": "~4.15.0",
|
||||
"@ionic-native/network": "^4.17.0",
|
||||
"@ionic-native/splash-screen": "~4.15.0",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"@ngx-translate/http-loader": "^2.0.1",
|
||||
"cordova-android": "7.1.1",
|
||||
"cordova-browser": "5.0.4",
|
||||
"cordova-plugin-app-version": "0.1.9",
|
||||
"cordova-plugin-device": "^2.0.2",
|
||||
"cordova-plugin-ionic-keyboard": "^2.1.3",
|
||||
"cordova-plugin-ionic-webview": "^2.3.3",
|
||||
|
@ -66,11 +68,12 @@
|
|||
},
|
||||
"cordova-plugin-ionic-keyboard": {},
|
||||
"wifiwizard2": {},
|
||||
"cordova-plugin-network-information": {}
|
||||
"cordova-plugin-network-information": {},
|
||||
"cordova-plugin-app-version": {}
|
||||
},
|
||||
"platforms": [
|
||||
"android",
|
||||
"browser"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|||
import { openSenseApp } from './app.component';
|
||||
import { OtaWizardPageModule } from '../pages/ota-wizard/ota-wizard.module';
|
||||
import { BlocklyPageModule } from '../pages/blockly/blockly.module';
|
||||
import { LoggingProvider } from '../providers/logging/logging';
|
||||
import { AppVersion } from '@ionic-native/app-version/ngx';
|
||||
import { StorageProvider } from '../providers/storage/storage';
|
||||
|
||||
// For AoT compilation (production builds) we need to have a factory for the loader of translation files.
|
||||
|
@ -45,6 +47,8 @@ export function createTranslateLoader(http: HttpClient) {
|
|||
StatusBar,
|
||||
SplashScreen,
|
||||
{provide: ErrorHandler, useClass: IonicErrorHandler},
|
||||
AppVersion,
|
||||
LoggingProvider,
|
||||
StorageProvider,
|
||||
]
|
||||
})
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { LogLevel, LogOptions } from "./providers/logging/logging";
|
||||
|
||||
export const COLORS = {
|
||||
PRIMARY: '#4EAF47', // sensebox green
|
||||
}
|
||||
export const DEFAULT_LANG = 'en'
|
||||
export const LOG_OPTIONS: LogOptions = {
|
||||
local: LogLevel.INFO,
|
||||
remote: LogLevel.WARN,
|
||||
endpoint: 'https://logs.snsbx.nroo.de/log',
|
||||
}
|
||||
|
|
125
src/providers/logging/logging.ts
Normal file
125
src/providers/logging/logging.ts
Normal file
|
@ -0,0 +1,125 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppVersion } from '@ionic-native/app-version/ngx';
|
||||
import { Platform } from 'ionic-angular';
|
||||
|
||||
import { LOG_OPTIONS } from '../../constants';
|
||||
import { StorageProvider, SETTINGS } from '../storage/storage';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
// these types must be defined here to avoid a circular dependency with LoggingProvider
|
||||
export interface LogOptions {
|
||||
local: boolean | LogLevel,
|
||||
remote: boolean | LogLevel,
|
||||
endpoint: string,
|
||||
}
|
||||
export enum LogLevel {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3,
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class LoggingProvider {
|
||||
private opts: LogOptions = LOG_OPTIONS
|
||||
private defaultFields: any = {}
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private plt: Platform,
|
||||
private version: AppVersion,
|
||||
private storage: StorageProvider,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
if ((<any>window).cordova) {
|
||||
this.version.getPackageName()
|
||||
.then(name => this.defaultFields.app = name)
|
||||
this.version.getVersionNumber()
|
||||
.then(version => this.defaultFields.appVersion = version)
|
||||
}
|
||||
this.defaultFields.platform = this.plt.platforms().join(' ')
|
||||
this.defaultFields.platformVersion = this.plt.version().str
|
||||
this.defaultFields.lang = translate.currentLang
|
||||
}
|
||||
|
||||
createChild (component: string, defaultFields: object = {}) {
|
||||
const child = new LoggingProvider(this.http, this.plt, this.version, this.storage, this.translate)
|
||||
Object.assign(child.defaultFields, defaultFields, { component })
|
||||
return child
|
||||
}
|
||||
|
||||
debug (...data) { return this.log(LogLevel.DEBUG, ...data) }
|
||||
info (...data) { return this.log(LogLevel.INFO, ...data) }
|
||||
warn (...data) { return this.log(LogLevel.WARN, ...data) }
|
||||
error (...data) { return this.log(LogLevel.ERROR, ...data) }
|
||||
|
||||
private log (level: LogLevel, ...fields: (string | object)[]): LogMessage {
|
||||
const msg = this.buildLogMessage(level, ...fields)
|
||||
|
||||
if (this.opts.local !== false && level >= this.opts.local) {
|
||||
this.getLocalLogFunc(msg.level)(msg.time, msg.msg, msg)
|
||||
}
|
||||
|
||||
if (this.opts.remote !== false && level >= this.opts.remote) {
|
||||
if (this.storage.get(SETTINGS).logOptin) {
|
||||
// fire & forget, no async handling as logging should not have impact on application flow
|
||||
this.http.post(this.opts.endpoint, msg, { responseType: 'text' })
|
||||
.toPromise()
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
private buildLogMessage (level: LogLevel, ...fields: (string | object)[]): LogMessage {
|
||||
const logentry = { } as LogMessage
|
||||
let msg = ''
|
||||
|
||||
for (const param of fields) {
|
||||
if (typeof param === 'object')
|
||||
Object.assign(logentry, param)
|
||||
else
|
||||
msg = msg ? `${msg} ${param}` : param
|
||||
}
|
||||
|
||||
if (msg)
|
||||
logentry.msg = msg
|
||||
|
||||
Object.assign(logentry, this.defaultFields, {
|
||||
time: Date.now(),
|
||||
level,
|
||||
})
|
||||
|
||||
return logentry
|
||||
}
|
||||
|
||||
private getLocalLogFunc (level: LogLevel): (...params: any[]) => any {
|
||||
switch (level) {
|
||||
case LogLevel.DEBUG:
|
||||
case LogLevel.INFO:
|
||||
return console.log
|
||||
|
||||
case LogLevel.WARN:
|
||||
return console.warn
|
||||
|
||||
case LogLevel.ERROR:
|
||||
default:
|
||||
return console.error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface LogMessage {
|
||||
level: LogLevel,
|
||||
time: Date,
|
||||
app: string,
|
||||
appVersion: string,
|
||||
platform: string,
|
||||
platformVersion: string,
|
||||
|
||||
component?: string,
|
||||
msg?: string,
|
||||
[k: string]: any,
|
||||
}
|
65
tools/logserver.js
Normal file
65
tools/logserver.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
'use strict';
|
||||
|
||||
const http = require('http')
|
||||
const fs = require('fs')
|
||||
|
||||
const port = process.argv[2] || 4444
|
||||
const logfile = process.argv[3] || '/tmp/logs.json'
|
||||
|
||||
const resHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'content-type',
|
||||
'Content-Type': 'text/plain',
|
||||
}
|
||||
|
||||
const handleLog = (req, res) => {
|
||||
let body = ''
|
||||
req.on('data', chunk => {
|
||||
body += chunk.toString()
|
||||
})
|
||||
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const msg = JSON.parse(body)
|
||||
msg.logclient = {
|
||||
ip: req.connection.remoteAddress,
|
||||
ua: req.headers['user-agent']
|
||||
}
|
||||
fileStream.write(JSON.stringify(msg))
|
||||
fileStream.write('\n')
|
||||
|
||||
res.writeHead(200, 'ok', resHeaders)
|
||||
res.end('ok')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
res.writeHead(400)
|
||||
res.end('invalid payload')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const requestHandler = (req, res) => {
|
||||
console.log(req.method, req.url)
|
||||
switch(req.method) {
|
||||
case 'OPTIONS':
|
||||
res.writeHead(200, 'ok', resHeaders)
|
||||
return res.end('ok')
|
||||
case 'POST':
|
||||
if (req.url === '/log') return handleLog(req, res)
|
||||
default:
|
||||
res.writeHead(404)
|
||||
return res.end('not found')
|
||||
}
|
||||
}
|
||||
|
||||
const fileStream = fs.createWriteStream(logfile, 'utf-8')
|
||||
const server = http.createServer(requestHandler)
|
||||
|
||||
server.listen(port, err => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('listening on', port)
|
||||
console.log('writing to', logfile)
|
||||
})
|
Loading…
Add table
Reference in a new issue