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
ios
Norwin 5 years ago
parent 816d672383
commit 8010bd0a0c

@ -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

@ -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',
}

@ -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,
}

@ -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…
Cancel
Save