/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow * @format * @oncall react_native */ import type {BundleOptions} from 'metro/private/shared/types'; import EventEmitter from 'events'; import os from 'os'; // eslint-disable-next-line import/no-commonjs const VERSION = require('../package.json').version; export type ActionLogEntryData = { action_name: string, log_entry_label?: string, ... }; export type ActionStartLogEntry = { action_name?: string, action_phase?: string, log_entry_label: string, log_session?: string, start_timestamp?: [number, number], ... }; export type LogEntry = { action_name?: string, action_phase?: string, action_result?: string, duration_ms?: number, entry_point?: string, file_name?: string, log_entry_label: string, log_session?: string, start_timestamp?: [number, number], outdated_modules?: number, bundle_size?: number, bundle_options?: BundleOptions, bundle_hash?: string, build_id?: string, error_message?: string, error_stack?: string, ... }; const log_session = `${os.hostname()}-${Date.now()}`; const eventEmitter = new EventEmitter(); function on(event: string, handler: (logEntry: LogEntry) => void): void { eventEmitter.on(event, handler); } function createEntry(data: LogEntry | string): LogEntry { const logEntry: LogEntry = typeof data === 'string' ? {log_entry_label: data} : data; return { ...logEntry, log_session, metro_bundler_version: VERSION, }; } function createActionStartEntry(data: ActionLogEntryData | string): LogEntry { const logEntry = typeof data === 'string' ? {action_name: data} : data; const {action_name} = logEntry; return createEntry({ log_entry_label: action_name, ...logEntry, action_name, action_phase: 'start', start_timestamp: process.hrtime(), }); } function createActionEndEntry( logEntry: ActionStartLogEntry, error?: ?Error, ): LogEntry { const {action_name, action_phase, start_timestamp} = logEntry; if (action_phase !== 'start' || !Array.isArray(start_timestamp)) { throw new Error('Action has not started or has already ended'); } const timeDelta = process.hrtime(start_timestamp); const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6); return createEntry({ log_entry_label: action_name as ?string, ...logEntry, action_name, action_phase: 'end', duration_ms, ...(error != null ? {error_message: error.message, error_stack: error.stack} : null), }); } function log(logEntry: LogEntry): LogEntry { eventEmitter.emit('log', logEntry); return logEntry; } export {on, createEntry, createActionStartEntry, createActionEndEntry, log};