Skip to content

Commit

Permalink
chore: Improve log verbosity (#577)
Browse files Browse the repository at this point in the history
Signed-off-by: Ivo Yankov <ivo@devlabs.bg>
  • Loading branch information
Ivo-Yankov authored Jul 25, 2024
1 parent c2a0a10 commit 52dc948
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 15 deletions.
24 changes: 17 additions & 7 deletions src/services/ConnectionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { LoggerService } from './LoggerService';
import { ServiceLocator } from './ServiceLocator';
import { CLIService } from './CLIService';
import { Errors } from '../Errors/LocalNodeErrors';
import debounce from '../utils/debounce';

/**
* ConnectionService is a service class that handles network connections.
Expand Down Expand Up @@ -52,6 +53,9 @@ export class ConnectionService implements IService{
*/
private cliService: CLIService;

// Debounced function to print error message at most once every N seconds
private readonly debouncedErrorLog;

/**
* Constructs a new instance of the ConnectionService.
* Initializes the logger and CLI service, and logs the initialization of the connection service.
Expand All @@ -61,6 +65,11 @@ export class ConnectionService implements IService{
this.logger = ServiceLocator.Current.get<LoggerService>(LoggerService.name);
this.cliService = ServiceLocator.Current.get<CLIService>(CLIService.name);
this.logger.trace('Connection Service Initialized!', this.serviceName);

this.debouncedErrorLog = debounce((message: string) => {
this.logger.info(message, this.serviceName);
}, 5000);

}

/**
Expand All @@ -72,7 +81,7 @@ export class ConnectionService implements IService{
* @returns {Promise<void>} A promise that resolves when the port is ready for connection.
* @public
*/
public async waitForFiringUp(port: number): Promise<void> {
public async waitForFiringUp(port: number, serviceName: string): Promise<void> {
const { host } = this.cliService.getCurrentArgv();
let isReady = false;
// this means that we wait around 100 seconds, normal consensus node startup takes around 60 seconds
Expand All @@ -83,12 +92,13 @@ export class ConnectionService implements IService{
.on('data', () => {
isReady = true;
})
.on('error', (err) => {
this.logger.trace(
`Waiting for the containers at ${host}:${port}, retrying in 0.1 seconds...`,
this.serviceName
);
this.logger.error(err.message, this.serviceName);
.on('error', (err: any) => {
if (err.code === 'ECONNREFUSED') {
this.debouncedErrorLog(`${serviceName} not yet available at: ${host}:${port}. Retrying...`);
}
else {
this.logger.error(err.message, this.serviceName);
}
});

retries--;
Expand Down
26 changes: 20 additions & 6 deletions src/services/DockerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,23 @@ export class DockerService implements IService{
const recommendedMemory = isMultiNodeMode ? MIN_MEMORY_MULTI_MODE : MIN_MEMORY_SINGLE_MODE;
this.logger.error(`Your docker memory resources are set to ${dockerMemory.toFixed(2)}GB. This is not enough, set to at least ${recommendedMemory}GB`, this.serviceName);
}


private logShellOutput(shellExec: any) {
[shellExec.stdout, shellExec.stderr].forEach( (output: string) => {
output.split("\n").map((line: string) => {
if (line === "") return;
this.logger.debug(line, this.serviceName);
});
});
}

private async executeExternal(command: string, options = {}): Promise<shell.ShellString> {
this.logger.trace(`🚀 Executing command: ${command}`, this.serviceName);
const shellExec = shell.exec(command, options);
this.logShellOutput(shellExec);
return shellExec;
}

/**
* Returns a Docker container object for the given container label.
*
Expand Down Expand Up @@ -352,9 +368,7 @@ export class DockerService implements IService{
composeFiles.push(...this.getUserComposeFiles(userComposeDir));
}

return shell.exec(
`docker compose -f ${composeFiles.join(' -f ')} up -d 2>${this.getNullOutput()}`
);
return this.executeExternal(`docker compose -f ${composeFiles.join(' -f ')} up -d`, {silent: true});
}

/**
Expand Down Expand Up @@ -388,8 +402,8 @@ export class DockerService implements IService{
public async tryDockerRecovery(stateName: string): Promise<void> {
const nullOutput = this.getNullOutput();
this.logger.trace('Stopping the docker containers...', stateName);
shell.exec(`docker compose kill --remove-orphans 2>${nullOutput}`);
shell.exec(`docker compose down -v --remove-orphans 2>${nullOutput}`);
this.executeExternal(`docker compose kill --remove-orphans`, {silent: true});
this.executeExternal(`docker compose down -v --remove-orphans`, {silent: true});
this.logger.trace('Cleaning the volumes and temp files...', stateName);
shell.exec(`rm -rf network-logs/* >${nullOutput} 2>&1`);
SafeDockerNetworkRemover.removeAll();
Expand Down
4 changes: 2 additions & 2 deletions src/state/StartState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ export class StartState implements IState{
this.logger.info(START_STATE_STARTED_DETECTING, this.stateName);

try {
await this.connectionService.waitForFiringUp(5600);
await this.connectionService.waitForFiringUp(50211);
await this.connectionService.waitForFiringUp(5600, 'Mirror Node GRPC');
await this.connectionService.waitForFiringUp(50211, 'Network Node');
} catch (e: any) {
if (e instanceof LocalNodeErrors) {
this.logger.error(e.message, this.stateName);
Expand Down
37 changes: 37 additions & 0 deletions src/utils/debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*-
*
* Hedera Local Node
*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

/**
* Limit the execution of a function to once every N ms
*/
const debounce = <F extends (...args: any[]) => void>(func: F, delay: number) => {
let timerActive = false;
return (...args: any) => {
if (!timerActive) {
timerActive = true;
func(...args);
setTimeout(() => {
timerActive = false;
}, delay);
}
};
};

export default debounce;

0 comments on commit 52dc948

Please sign in to comment.