Permalink
Browse files

feat(usage): memory usage

  • Loading branch information...
jkuri committed Sep 14, 2017
1 parent 2c9be48 commit 02f06b259bf80a36f4d196906bbc5db91ba27f7b
@@ -24,11 +24,17 @@ import { readFileSync } from 'fs';
import * as express from 'express';
import { yellow, red, blue, green } from 'chalk';
import { sessionParser } from './server';
import { IMemoryData, memory } from './stats/memory';

export interface ISocketServerOptions {
port: number;
}

export interface IOutput {
type: string;
data: IMemoryData;
}

export class SocketServer {
options: ISocketServerOptions;
connections: Observable<any>;
@@ -129,6 +135,7 @@ export class SocketServer {
conn.next({ type: 'job stopped', data: event.data.jobId });
});
break;

case 'subscribeToJobOutput':
getJobProcesses()
.then(procs => {
@@ -147,15 +154,23 @@ export class SocketServer {
.filter(e => e.job_id === parseInt(event.data.jobId, 10))
.subscribe(output => conn.next(output));
break;

case 'subscribeToLogs':
logger.subscribe(msg => conn.next(msg));
break;

case 'subscribeToNotifications':
logger.filter(msg => !!msg.notify).subscribe(msg => {
const notify = { notification: msg, type: 'notification' };
conn.next(notify);
});
break;

case 'subscribeToStats':
memory()
.subscribe(event => conn.next(event));

break;
}
});
});
@@ -0,0 +1,26 @@
import { totalmem, freemem } from 'os';
import { Observable } from 'rxjs';
import { IOutput } from '../socket';

export interface IMemoryData {
total: number;
free: number;
}

export function memory(): Observable<IOutput> {
return Observable.timer(0, 2000)
.timeInterval()
.mergeMap(() => getMemory())
.map((res: IMemoryData) => {
return { type: 'memory', data: res };
});
}

function getMemory(): Promise<IMemoryData> {
return new Promise(resolve => {
resolve({
total: totalmem(),
free: freemem()
});
});
}
@@ -13,6 +13,7 @@ import { AuthGuardProvider, AuthGuard } from './services/auth-guard.service';
import { AccessGuardProvider, AccessGuard } from './services/access-guard.service';
import { AuthServiceProvider } from './services/auth.service';
import { NotificationService } from './services/notification.service';
import { StatsService } from './services/stats.service';
import { EqualValidator } from './directives/equal-validator.directive';
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
import { ToTimePipe } from './pipes/to-time.pipe';
@@ -134,7 +135,8 @@ import { AppLineChartComponent } from './components/app-line-chart';
AuthServiceProvider,
AuthGuardProvider,
AccessGuardProvider,
NotificationService
NotificationService,
StatsService
],
bootstrap: [ AppComponent ]
})
@@ -23,6 +23,7 @@ <h1>Dashboard</h1>
</div>

<div class="columns is-multiline" *ngIf="!loading">

<div class="column is-8">
<div class="content-box">
<app-line-chart></app-line-chart>
@@ -33,9 +34,40 @@ <h1>Dashboard</h1>

</div>
<div class="content-box">

<h2>Memory</h2>
<div class="info-data-container">
<span class="label-txt">Total</span>
<span class="data-txt">{{ memoryHuman?.total }}</span>
</div>
<div class="info-data-container">
<span class="label-txt">Used</span>
<span class="data-txt">{{ memoryHuman?.used }}</span>
</div>
<div class="info-data-container">
<span class="label-txt">Free</span>
<span class="data-txt">{{ memoryHuman?.free }}</span>
</div>
<hr/>
<div class="info-data-container">
<span class="label-txt">Usage</span>
<div class="data-progress-container green">
<span class="data-progress-bar" [style.width]="memoryPercentage + '%'"></span>
</div>
<span class="data-txt">
{{ memoryPercentage }}%
</span>
</div>
</div>
</div>

<div class="column is-6">
<div class="content-box"></div>
</div>

<div class="column is-6">
<div class="content-box"></div>
</div>

</div>
</div>
</div>
@@ -1,22 +1,55 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SocketService } from '../../services/socket.service';
import { StatsService } from '../../services/stats.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
selector: 'app-dashboard',
templateUrl: 'app-dashboard.component.html'
})
export class AppDashboardComponent implements OnInit, OnDestroy {
loading: boolean;
sub: Subscription;
memory: { total: number, free: number, used: number };
memoryHuman: { total: string, free: string, used: string };
memoryPercentage: string;

constructor(private socketService: SocketService) {
constructor(private statsService: StatsService) {
this.loading = true;

this.memory = { total: null, free: null, used: null };
this.memoryHuman = { total: null, free: null, used: null };
this.memoryPercentage = '0';
}

ngOnInit() {
this.loading = false;

this.sub = this.statsService.stats.subscribe(e => {
if (e.type === 'memory') {
this.memory = {
total: e.data.total,
free: e.data.free,
used: e.data.total - e.data.free
};

this.memoryHuman = {
total: this.statsService.humanizeBytes(e.data.total),
free: this.statsService.humanizeBytes(e.data.free),
used: this.statsService.humanizeBytes(e.data.total - e.data.free)
};

this.memoryPercentage = Number(this.memory.used / this.memory.total * 100).toFixed(2);
}
});

setTimeout(() => this.statsService.start());
}

ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}

this.statsService.stop();
}
}
@@ -12,7 +12,8 @@ import {
max,
axisBottom,
axisLeft,
area
area,
event
} from 'd3';

@Component({
@@ -22,6 +23,7 @@ import {
export class AppLineChartComponent implements OnInit, OnDestroy {
data: any[];
el: Element;
tooltip: any;

constructor(private elementRef: ElementRef) {
this.data = [
@@ -106,13 +108,7 @@ export class AppLineChartComponent implements OnInit, OnDestroy {

g.append('g')
.attr('class', 'axis axis--y')
.call(axisLeft(y))
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '0.71em')
.attr('fill', 'white')
.text('Temperature, ºC');
.call(axisLeft(y));

g.append('path')
.attr('d', l(this.data))
@@ -123,5 +119,15 @@ export class AppLineChartComponent implements OnInit, OnDestroy {
g.append('path')
.attr('d', a(this.data))
.attr('fill', 'url(#mainGradient)');

g.selectAll('dot')
.data(this.data)
.enter().append('circle')
.attr('r', 3)
.attr('cx', (d: any) => x(d.date))
.attr('cy', (d: any) => y(d.value))
.attr('fill', 'white')
.attr('stroke', '#00AAFF')
.attr('stroke-width', 2);
}
}
@@ -0,0 +1,42 @@
import { Injectable, Provider, EventEmitter } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { SocketService } from './socket.service';

@Injectable()
export class StatsService {
stats: EventEmitter<any>;
sub: Subscription;

constructor(private socketService: SocketService) {
this.stats = new EventEmitter<any>();
}

start(): void {
this.sub = this.socketService.onMessage()
.filter(e => e.type === 'memory')
.subscribe(e => this.stats.emit(e));

this.socketService.emit({ type: 'subscribeToStats' });
}

stop(): void {
if (this.sub) {
this.sub.unsubscribe();
}

this.socketService.emit({ type: 'unsubscribeFromStats' });
}

humanizeBytes(bytes: number): string {
if (bytes === 0) {
return '0 Byte';
}

const k = 1024;
const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
const i: number = Math.floor(Math.log(bytes) / Math.log(k));

return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
}
@@ -404,6 +404,47 @@ progress
min-height: 215px
margin-bottom: 30px

h2
padding: 0
margin: 0 0 10px 0

hr
margin: 10px 0

.info-data-container
display: flex
align-items: center
height: 30px
font-size: 14px
justify-content: space-between

.label-txt
color: $color

.data-txt
color: $color-secondary

.data-progress-container
height: 4px
border-radius: 2px
width: 100%
background: #E2E7EE
position: relative
margin: 0 30px 0 20px

.data-progress-bar
height: 4px
border-radius: 2px
position: absolute
left: 0
top: 0

&.green

.data-progress-bar
background-image: linear-gradient(90deg, #2BB415 2%, #5AD946 98%)


.line-chart-container
width: 100%
height: 400px

0 comments on commit 02f06b2

Please sign in to comment.