Skip to content

Commit 6b292b9

Browse files
committed
Log view improvements
1 parent 7754428 commit 6b292b9

10 files changed

+180
-46
lines changed

API/APIServer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(self, trade_handler: TradeHandler):
4646
self.api.add_resource(ProxyEndpoint, APIServer.API_PREFIX + '/proxy/icon',
4747
resource_class_kwargs={'trade_handler': self.th})
4848

49-
self.api.add_resource(LogsEndpoint, APIServer.API_PREFIX + '/logs/<file>',
49+
self.api.add_resource(LogsEndpoint, APIServer.API_PREFIX + '/logs',
5050
resource_class_kwargs={'trade_handler': self.th})
5151

5252
self.api.add_resource(OrderBookEndpoint, APIServer.API_PREFIX + '/orderbook/<symbol>',

API/Endpoints/LogsEndpoint.py

+47-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,57 @@
1+
import datetime
2+
from flask import Request, request
3+
14
from API.Endpoints.BotAPIResource import BotAPIResource
25
from flask_jwt_extended import jwt_required
6+
import arrow
37

48

59
class LogsEndpoint(BotAPIResource):
610

711
@jwt_required()
8-
def get(self, file):
12+
def get(self, file=None, limit=1000):
13+
14+
file = request.args.get('file', file)
15+
limit = int(request.args.get('limit', 1000))
16+
917
if not file:
1018
return self.list_files()
1119

12-
return self.get_file_contents(None if file == 'latest' else file)
20+
contents = self.get_file_contents(None if file == 'latest' else file)
21+
22+
log_records = []
23+
for line in contents.splitlines():
24+
if not line:
25+
continue
26+
27+
level_start = -1
28+
if '[' in line:
29+
level_start = line.index('[')
30+
31+
if level_start > -1:
32+
log_entry = {}
33+
34+
dt_str = line[0:level_start]
35+
try:
36+
log_entry['d'] = arrow.get(dt_str).format('YYYY-MM-DD HH:mm:ss.SSS')
37+
except Exception as e:
38+
log_records[-1]['t'] += '\n' + line.strip()
39+
continue
40+
41+
level_end = line.index(']')
42+
log_entry['l'] = line[level_start + 1: level_end]
43+
44+
orig_start = line.index('[', level_end)
45+
orig_end = line.index(']', level_end+1)
46+
47+
log_entry['o'] = line[orig_start+1:orig_end]
48+
49+
log_start = line.index(':', orig_end+1)
50+
51+
log_entry['t'] = line[log_start +1:].strip()
52+
log_records.append(log_entry)
53+
else:
54+
log_records[-1]['t'] += '\n' + line.strip()
55+
56+
57+
return log_records[::-1][0:limit]

admin/src/app/balances/balances.component.html

+2-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="balance-table">
1+
<div>
22
<table mat-table [dataSource]="balances" matSort class="mat-elevation-z2">
33

44
<ng-container matColumnDef="sym">
@@ -23,17 +23,7 @@
2323

2424
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
2525
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
26-
</table>
27-
28-
<ng-template #modal_template>
29-
<h2 mat-dialog-title>{{isCloseTradeAction ? 'Cancel' : 'Delete'}} {{'"' + selectedTrade.sym + '"'}} Trade?</h2>
30-
<div mat-dialog-content>Please confirm if you want to {{isCloseTradeAction ? 'cancel' : 'delete' }} this trade</div>
31-
<div mat-dialog-actions align="end">
32-
<button mat-button mat-dialog-close="cancel" (click)="decline()" color="primary" cdkFocusInitial>No</button>
33-
<button mat-button mat-dialog-close="ok" (click)="confirm()" color="primary">Yes</button>
34-
</div>
35-
</ng-template>
36-
26+
</table>
3727
</div>
3828

3929

admin/src/app/botapi.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {TradeDetails, Entry} from './trade-details';
88
import deleteProperty = Reflect.deleteProperty;
99
import {environment} from '../environments/environment';
1010
import { Balance } from './balance';
11+
import { LogEntry } from './log-entry';
12+
import * as moment from 'moment';
1113

1214

1315
const httpOptions = {
@@ -30,10 +32,22 @@ export class BotApi {
3032
constructor(private http: HttpClient) {
3133
}
3234

33-
getRecentLogFileContents(filename='latest'): Observable<String> {
34-
return this.http.get<String>(`${this.API_URL}/logs/${filename}`).pipe(
35+
getRecentLogFileContents(limit:number=1000, filename='latest'): Observable<LogEntry[]> {
36+
return this.http.get<LogEntry[]>(`${this.API_URL}/logs?file=${filename}&limit=${limit}`).pipe(
3537
retry(this.RETRIES),
36-
catchError(this.handleError('getActiveTrades', '')));
38+
map(r => {
39+
40+
const entries: LogEntry[] = [];
41+
42+
r.forEach(entry => {
43+
entry.d = new Date(entry.d)
44+
entries.push(new LogEntry(entry));
45+
});
46+
// console.log(entries);
47+
return entries;
48+
49+
}),
50+
catchError(this.handleError('getActiveTrades',[])));
3751
}
3852

3953
addTrade(trade: TradeDetails ): Observable<ApiResult> {

admin/src/app/log-entry.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { LogEntry } from './log-entry';
2+
3+
describe('LogEntry', () => {
4+
it('should create an instance', () => {
5+
expect(new LogEntry()).toBeTruthy();
6+
});
7+
});

admin/src/app/log-entry.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export class LogEntry {
2+
3+
d: Date;
4+
l: string;
5+
t: string;
6+
o: string;
7+
8+
constructor(init?: Partial<LogEntry>){
9+
Object.assign(this, init)
10+
}
11+
12+
get date(): Date{
13+
return this.d;
14+
}
15+
16+
get level(): string{
17+
return this.l;
18+
}
19+
20+
get originator(): string{
21+
return this.o;
22+
}
23+
24+
get text(): string{
25+
return this.t;
26+
}
27+
}

admin/src/app/logs/logs.component.css

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
/* make-scrollable {
2-
height: 800px;
3-
overflow-y: scroll;
4-
} */
5-
6-
.log-item {
7-
height: 32px;
8-
font-size: 16px;
9-
}
10-
11-
div {
1+
.top-panel-ctrl {
122
display: flex;
133
flex-direction: row;
144
align-items: center;
155
box-sizing: border-box;
166
padding: 0 16px;
177
position: relative;
188
height: inherit;
9+
}
10+
11+
table {
12+
width: 100%;
1913
}
+38-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
1-
<!-- <div class="full-width">
2-
<mat-list *ngFor="let log of logContents">
3-
<mat-list-item class="log-item"><p>{{log}}</p></mat-list-item>
4-
</mat-list>
5-
</div> -->
6-
<div class="full-width">
1+
<div class="full-width top-panel-ctrl">
2+
<mat-form-field appearance="standard">
3+
<mat-label># of log entries</mat-label>
4+
<input matInput type="number" min="1" step="1" id="targetPrice" name="targetPrice" [(ngModel)]="limit">
5+
</mat-form-field>
76
<button mat-icon-button matTooltip="Refresh Log" (click)="refreshLogs()">
87
<mat-icon>refresh</mat-icon>
98
</button>
10-
<button mat-icon-button matTooltip="Copy Log" *ngIf="logContents">
11-
<mat-icon [cdkCopyToClipboard]="this.logContents.join('\n')">file_copy</mat-icon>
9+
<button mat-icon-button matTooltip="Copy All Log Records" *ngIf="logs">
10+
<mat-icon [cdkCopyToClipboard]="getClipboard()">file_copy</mat-icon>
1211
</button>
1312
</div>
14-
<div class="full-width" *ngFor="let log of logContents">
15-
<p>{{log}}</p>
16-
</div>
13+
14+
<mat-progress-bar *ngIf="!logs" mode="query"></mat-progress-bar>
15+
<div>
16+
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z2">
17+
18+
<ng-container matColumnDef="date">
19+
<th mat-header-cell *matHeaderCellDef mat-sort-header="d"> Date </th>
20+
<td mat-cell *matCellDef="let log"> {{log.d.toLocaleString()}} </td>
21+
</ng-container>
22+
23+
<ng-container matColumnDef="level">
24+
<th mat-header-cell *matHeaderCellDef mat-sort-header="l"> Level </th>
25+
<td mat-cell *matCellDef="let log"> {{log.l}} </td>
26+
</ng-container>
27+
28+
<ng-container matColumnDef="origin">
29+
<th mat-header-cell *matHeaderCellDef mat-sort-header="o"> Origin </th>
30+
<td mat-cell *matCellDef="let log"> {{log.o}} </td>
31+
</ng-container>
32+
33+
<ng-container matColumnDef="message">
34+
<th mat-header-cell *matHeaderCellDef> Message </th>
35+
<td mat-cell *matCellDef="let log"> {{log.t}} </td>
36+
</ng-container>
37+
38+
<tr mat-header-row *matHeaderRowDef="['date', 'level', 'origin', 'message']"></tr>
39+
<tr mat-row *matRowDef="let row; columns: ['date', 'level', 'origin', 'message'];"></tr>
40+
</table>
41+
42+
</div>
43+
<mat-paginator [pageSizeOptions]="[30, 50, 100]" showFirstLastButtons></mat-paginator>

admin/src/app/logs/logs.component.ts

+35-6
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,56 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
2+
import { MatPaginator } from '@angular/material/paginator';
3+
import { MatSort } from '@angular/material/sort';
4+
import { MatTableDataSource } from '@angular/material/table';
25
import { BotApi } from '../botapi';
6+
import { LogEntry } from '../log-entry';
37

48
@Component({
59
selector: 'app-logs',
610
templateUrl: './logs.component.html',
711
styleUrls: ['./logs.component.css']
812
})
9-
export class LogsComponent implements OnInit {
13+
export class LogsComponent implements OnInit, AfterViewInit {
14+
15+
logs: LogEntry[];
16+
dataSource: MatTableDataSource<LogEntry> = new MatTableDataSource<LogEntry>();
17+
limit: number = 1000;
18+
@ViewChild(MatPaginator) paginator: MatPaginator;
19+
@ViewChild(MatSort) sort: MatSort;
1020

11-
logContents: String[];
1221

1322
constructor(private api: BotApi) { }
23+
ngAfterViewInit(): void {
24+
this.refreshLogs();
25+
console.log(this.paginator);
26+
}
1427

1528
ngOnInit(): void {
16-
this.refreshLogs();
29+
// this.refreshLogs();
1730
}
1831

1932
refreshLogs(): void {
20-
this.api.getRecentLogFileContents().subscribe(r => {
33+
this.logs = null;
34+
this.api.getRecentLogFileContents(this.limit).subscribe(r => {
2135
if (r){
22-
this.logContents = r.trim().split('\n').reverse();
36+
this.logs = r;
37+
this.dataSource = new MatTableDataSource<LogEntry>(this.logs);
38+
this.dataSource.paginator = this.paginator;
39+
this.dataSource.sort = this.sort;
2340
}
2441
})
2542
}
2643

44+
getClipboard(): string {
45+
let cb = ''
46+
47+
if (this.logs){
48+
this.logs.forEach(l=> {
49+
cb += `${l.d.toLocaleString()} [${l.l}] [${l.o}]: ${l.t}\n`
50+
})
51+
}
52+
53+
return cb
54+
}
55+
2756
}

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Flask==1.1.2
88
Flask-Cors==3.0.10
99
Flask-JWT-Extended==4.2.0
1010
Flask-RESTful==0.3.8
11+
arrow==1.1.0

0 commit comments

Comments
 (0)