blob: e5dd18db611472910bc83a89027b611e7ee5df63 [file]
/*!
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
*/
import config from '../../../config/AgentConfig';
import { createLogger } from '../../../logging';
import BootService from './BootService';
import MeterSender from '../meter/MeterSender';
import GRPCChannelManager from '../remote/GRPCChannelManager';
import ServiceManagementClient from '../remote/ServiceManagementClient';
import TraceSegmentServiceClient from '../remote/TraceSegmentServiceClient';
const logger = createLogger(__filename);
/** Service registry and boot orchestrator (Java {@code ServiceManager}). */
class ServiceManager {
private static readonly instance = new ServiceManager();
private bootedServices = new Map<new (...args: never[]) => BootService, BootService>();
private booted = false;
static get INSTANCE(): ServiceManager {
return ServiceManager.instance;
}
boot(): void {
if (this.booted) {
return;
}
this.loadServices();
const services = this.sortByPriority(true);
for (const service of services) {
try {
service.prepare();
} catch (error) {
logger.error('ServiceManager prepare failed: %s', error);
}
}
for (const service of services) {
try {
service.boot();
} catch (error) {
logger.error('ServiceManager boot failed: %s', error);
}
}
for (const service of services) {
try {
service.onComplete();
} catch (error) {
logger.error('ServiceManager onComplete failed: %s', error);
}
}
this.booted = true;
}
shutdown(): void {
for (const service of this.sortByPriority(false)) {
try {
service.shutdown();
} catch (error) {
logger.error('ServiceManager shutdown failed: %s', error);
}
}
this.bootedServices.clear();
this.booted = false;
}
flush(): Promise<unknown> | null {
const traceFlush = this.findService(TraceSegmentServiceClient)?.flush() ?? null;
const meterSender = this.findService(MeterSender);
const meterFlush = meterSender?.flush() ?? null;
if (!traceFlush && !meterFlush) {
return null;
}
if (!traceFlush) {
return meterFlush;
}
if (!meterFlush) {
return traceFlush;
}
return Promise.all([traceFlush, meterFlush]).then(() => null);
}
findService<T extends BootService>(serviceClass: new (...args: never[]) => T): T | undefined {
return this.bootedServices.get(serviceClass) as T | undefined;
}
private register<T extends BootService>(serviceClass: new (...args: never[]) => T, service: T): void {
this.bootedServices.set(serviceClass, service);
}
private loadServices(): void {
this.register(GRPCChannelManager, new GRPCChannelManager());
this.register(TraceSegmentServiceClient, new TraceSegmentServiceClient());
this.register(ServiceManagementClient, new ServiceManagementClient());
if (config.runtimeMetricsReporterActive) {
this.register(MeterSender, new MeterSender());
}
}
private sortByPriority(ascending: boolean): BootService[] {
const services = Array.from(this.bootedServices.values());
services.sort((left, right) =>
ascending ? left.priority() - right.priority() : right.priority() - left.priority(),
);
return services;
}
}
export default ServiceManager;