Allow multiple services to be tailed with balena logs and push

Also correctly type the input.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@balena.io>
This commit is contained in:
Cameron Diver 2019-05-28 16:56:39 +01:00 committed by Paulo Castro
parent 0ee73f5164
commit b391c96e64
5 changed files with 61 additions and 35 deletions

@ -865,6 +865,7 @@ Examples:
$ balena logs 192.168.0.31
$ balena logs 192.168.0.31 --service my-service
$ balena logs 192.168.0.31 --service my-service-1 --service my-service-2
$ balena logs 23c73a1.local --system
$ balena logs 23c73a1.local --system --service my-service
@ -877,7 +878,8 @@ continuously stream output
#### --service, -s &#60;service&#62;
Only show logs for a single service. This can be used in combination with --system
Reject logs not originating from this service.
This can be used in combination with --system or other --service flags.
#### --system, -S
@ -1444,7 +1446,8 @@ Don't tail application logs when pushing to a local mode device
#### --service &#60;service&#62;
Only show logs from a single service. This can be used in combination with --system.
Reject logs not originating from this service.
This can be used in combination with --system and other --service flags.
Only valid when pushing to a local mode device.
#### --system

@ -38,9 +38,9 @@ export const logs: CommandDefinition<
uuidOrDevice: string;
},
{
tail: boolean;
service: string;
system: boolean;
tail?: boolean;
service?: [string] | string;
system?: boolean;
}
> = {
signature: 'logs <uuidOrDevice>',
@ -66,6 +66,7 @@ export const logs: CommandDefinition<
$ balena logs 192.168.0.31
$ balena logs 192.168.0.31 --service my-service
$ balena logs 192.168.0.31 --service my-service-1 --service my-service-2
$ balena logs 23c73a1.local --system
$ balena logs 23c73a1.local --system --service my-service`,
@ -78,8 +79,9 @@ export const logs: CommandDefinition<
},
{
signature: 'service',
description:
'Only show logs for a single service. This can be used in combination with --system',
description: stripIndent`
Reject logs not originating from this service.
This can be used in combination with --system or other --service flags.`,
parameter: 'service',
alias: 's',
},
@ -95,6 +97,7 @@ export const logs: CommandDefinition<
async action(params, options, done) {
normalizeUuidProp(params);
const balena = (await import('balena-sdk')).fromSharedOptions();
const isArray = await import('lodash/isArray');
const { serviceIdToName } = await import('../utils/cloud');
const { displayDeviceLogs, displayLogObject } = await import(
'../utils/device/logs'
@ -107,6 +110,13 @@ export const logs: CommandDefinition<
const logger = new Logger();
const servicesToDisplay =
options.service != null
? isArray(options.service)
? options.service
: [options.service]
: undefined;
const displayCloudLog = async (line: CloudLog) => {
if (!line.isSystem) {
let serviceName = await serviceIdToName(balena, line.serviceId);
@ -117,14 +127,14 @@ export const logs: CommandDefinition<
{ serviceName, ...line },
logger,
options.system || false,
options.service,
servicesToDisplay,
);
} else {
displayLogObject(
line,
logger,
options.system || false,
options.service,
servicesToDisplay,
);
}
};
@ -151,7 +161,7 @@ export const logs: CommandDefinition<
logStream,
logger,
options.system || false,
options.service,
servicesToDisplay,
);
} else {
exitIfNotLoggedIn();

@ -105,16 +105,16 @@ export const push: CommandDefinition<
applicationOrDevice_raw: string;
},
{
source: string;
emulated: boolean;
dockerfile: string; // DeviceDeployOptions.dockerfilePath (alternative Dockerfile)
nocache: boolean;
'registry-secrets': string;
live: boolean;
detached: boolean;
service: string;
system: boolean;
env: string | string[];
source?: string;
emulated?: boolean;
dockerfile?: string; // DeviceDeployOptions.dockerfilePath (alternative Dockerfile)
nocache?: boolean;
'registry-secrets'?: string;
live?: boolean;
detached?: boolean;
service?: string | string[];
system?: boolean;
env?: string | string[];
}
> = {
signature: 'push <applicationOrDevice>',
@ -215,7 +215,8 @@ export const push: CommandDefinition<
{
signature: 'service',
description: stripIndent`
Only show logs from a single service. This can be used in combination with --system.
Reject logs not originating from this service.
This can be used in combination with --system and other --service flags.
Only valid when pushing to a local mode device.`,
parameter: 'service',
},
@ -243,6 +244,7 @@ export const push: CommandDefinition<
async action(params, options, done) {
const sdk = (await import('balena-sdk')).fromSharedOptions();
const Bluebird = await import('bluebird');
const isArray = await import('lodash/isArray');
const remote = await import('../utils/remote-build');
const deviceDeploy = await import('../utils/device/deploy');
const { exitIfNotLoggedIn, exitWithExpectedError } = await import(
@ -312,8 +314,8 @@ export const push: CommandDefinition<
async (token, baseUrl, owner) => {
const opts = {
dockerfilePath,
emulated: options.emulated,
nocache: options.nocache,
emulated: options.emulated || false,
nocache: options.nocache || false,
registrySecrets,
};
const args = {
@ -332,6 +334,12 @@ export const push: CommandDefinition<
break;
case BuildTarget.Device:
const device = appOrDevice;
const servicesToDisplay =
options.service != null
? isArray(options.service)
? options.service
: [options.service]
: undefined;
// TODO: Support passing a different port
await Bluebird.resolve(
deviceDeploy.deployToDevice({
@ -342,7 +350,7 @@ export const push: CommandDefinition<
nocache: options.nocache || false,
live: options.live || false,
detached: options.detached || false,
service: options.service,
services: servicesToDisplay,
system: options.system || false,
env:
typeof options.env === 'string'

@ -48,7 +48,7 @@ export interface DeviceDeployOptions {
nocache: boolean;
live: boolean;
detached: boolean;
service?: string;
services?: string[];
system: boolean;
env: string[];
}
@ -236,7 +236,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
const logStream = await api.getLogStream();
globalLogger.logInfo('Streaming device logs...');
promises.push(
displayDeviceLogs(logStream, globalLogger, opts.system, opts.service),
displayDeviceLogs(logStream, globalLogger, opts.system, opts.services),
);
} else {
globalLogger.logLivepush(
@ -255,7 +255,12 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
// Now all we need to do is stream back the logs
const logStream = await api.getLogStream();
globalLogger.logInfo('Streaming device logs...');
await displayDeviceLogs(logStream, globalLogger, opts.system, opts.service);
await displayDeviceLogs(
logStream,
globalLogger,
opts.system,
opts.services,
);
}
}

@ -37,11 +37,11 @@ export function displayDeviceLogs(
logs: Readable,
logger: Logger,
system: boolean,
filterService?: string,
filterServices?: string[],
): Bluebird<void> {
return new Bluebird((resolve, reject) => {
logs.on('data', log => {
displayLogLine(log, logger, system, filterService);
displayLogLine(log, logger, system, filterServices);
});
logs.on('error', reject);
@ -64,11 +64,11 @@ function displayLogLine(
log: string | Buffer,
logger: Logger,
system: boolean,
filterService?: string,
filterServices?: string[],
): void {
try {
const obj: Log = JSON.parse(log.toString());
displayLogObject(obj, logger, system, filterService);
displayLogObject(obj, logger, system, filterServices);
} catch (e) {
logger.logDebug(`Dropping device log due to failed parsing: ${e}`);
}
@ -78,7 +78,7 @@ export function displayLogObject<T extends Log>(
obj: T,
logger: Logger,
system: boolean,
filterService?: string,
filterServices?: string[],
): void {
let toPrint: string;
if (obj.timestamp != null) {
@ -88,8 +88,8 @@ export function displayLogObject<T extends Log>(
}
if (obj.serviceName != null) {
if (filterService) {
if (obj.serviceName !== filterService) {
if (filterServices) {
if (!_.includes(filterServices, obj.serviceName)) {
return;
}
} else if (system) {
@ -99,7 +99,7 @@ export function displayLogObject<T extends Log>(
const colourFn = getServiceColourFn(obj.serviceName);
toPrint += ` ${colourFn(`[${obj.serviceName}]`)}`;
} else if (filterService != null && !system) {
} else if (filterServices != null && !system) {
// We have a system log here but we are filtering based
// on a service, so drop this too
return;