import { EventEmitter } from 'events';

declare enum LogLevel {
    OFF = 0,
    ERROR = 1,
    SOFT_FAIL = 2,
    WARNING = 3,
    INFO = 4,
    DEBUG = 5,
    PERF = 6
}
declare enum LogFormat {
    JSON = "JSON",
    TEXT = "TEXT"
}
declare const PREFIX_DELIMITER = ":";
declare const LEVELS: typeof LogLevel;
declare const LEVEL_TO_STRING: string[];
/**
 * A symbol used to mark a limited depth object as having come from an error
 * @internal
 */
declare const IS_APIFY_LOGGER_EXCEPTION: unique symbol;

/**
 * This is an abstract class that should
 * be extended by custom logger classes.
 *
 * this._log() method must be implemented by them.
 */
declare class Logger extends EventEmitter {
    protected options: Record<string, any>;
    constructor(options: Record<string, any>);
    setOptions(options: Record<string, any>): void;
    getOptions(): Record<string, any>;
    _outputWithConsole(level: LogLevel, line: string): void;
    _log(level: LogLevel, message: string, data?: any, exception?: unknown, opts?: Record<string, any>): void;
    log(level: LogLevel, message: string, ...args: any[]): void;
}

interface LoggerOptions {
    /**
     * Sets the log level to the given value, preventing messages from less important log levels
     * from being printed to the console. Use in conjunction with the `log.LEVELS` constants.
     */
    level?: number;
    /** Max depth of data object that will be logged. Anything deeper than the limit will be stripped off. */
    maxDepth?: number;
    /** Max length of the string to be logged. Longer strings will be truncated. */
    maxStringLength?: number;
    /** Prefix to be prepended the each logged line. */
    prefix?: string | null;
    /** Suffix that will be appended the each logged line. */
    suffix?: string | null;
    /**
     * Logger implementation to be used. Default one is log.LoggerText to log messages as easily readable
     * strings. Optionally you can use `log.LoggerJson` that formats each log line as a JSON.
     */
    logger?: Logger;
    /** Additional data to be added to each log line. */
    data?: Record<string, unknown>;
}
type AdditionalData = Record<string, any> | null;
/**
 * The log instance enables level aware logging of messages and we advise
 * to use it instead of `console.log()` and its aliases in most development
 * scenarios.
 *
 * A very useful use case for `log` is using `log.debug` liberally throughout
 * the codebase to get useful logging messages only when appropriate log level is set
 * and keeping the console tidy in production environments.
 *
 * The available logging levels are, in this order: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `OFF`
 * and can be referenced from the `log.LEVELS` constant, such as `log.LEVELS.ERROR`.
 *
 * To log messages to the system console, use the `log.level(message)` invocation,
 * such as `log.debug('this is a debug message')`.
 *
 * To prevent writing of messages above a certain log level to the console, simply
 * set the appropriate level. The default log level is `INFO`, which means that
 * `DEBUG` messages will not be printed, unless enabled.
 *
 * **Example:**
 * ```js
 * import log from '@apify/log';
 *
 * // importing from the Apify SDK or Crawlee is also supported:
 * // import { log } from 'apify';
 * // import { log } from 'crawlee';
 *
 * log.info('Information message', { someData: 123 }); // prints message
 * log.debug('Debug message', { debugData: 'hello' }); // doesn't print anything
 *
 * log.setLevel(log.LEVELS.DEBUG);
 * log.debug('Debug message'); // prints message
 *
 * log.setLevel(log.LEVELS.ERROR);
 * log.debug('Debug message'); // doesn't print anything
 * log.info('Info message'); // doesn't print anything
 * log.error('Error message', { errorDetails: 'This is bad!' }); // prints message
 *
 * try {
 *   throw new Error('Not good!');
 * } catch (e) {
 *   log.exception(e, 'Exception occurred', { errorDetails: 'This is really bad!' }); // prints message
 * }
 *
 * log.setOptions({ prefix: 'My actor' });
 * log.info('I am running!'); // prints "My actor: I am running"
 *
 * const childLog = log.child({ prefix: 'Crawler' });
 * log.info('I am crawling!'); // prints "My actor:Crawler: I am crawling"
 * ```
 *
 * Another very useful way of setting the log level is by setting the `APIFY_LOG_LEVEL`
 * environment variable, such as `APIFY_LOG_LEVEL=DEBUG`. This way, no code changes
 * are necessary to turn on your debug messages and start debugging right away.
 *
 * To add timestamps to your logs, you can override the default logger settings:
 * ```js
 * log.setOptions({
 *     logger: new log.LoggerText({ skipTime: false }),
 * });
 * ```
 * You can customize your logging further by extending or replacing the default
 * logger instances with your own implementations.
 */
declare class Log {
    /**
     * Map of available log levels that's useful for easy setting of appropriate log levels.
     * Each log level is represented internally by a number. Eg. `log.LEVELS.DEBUG === 5`.
     */
    readonly LEVELS: typeof LogLevel;
    private options;
    private readonly warningsOnceLogged;
    constructor(options?: Partial<LoggerOptions>);
    private _limitDepth;
    /**
     * Returns the currently selected logging level. This is useful for checking whether a message
     * will actually be printed to the console before one actually performs a resource intensive operation
     * to construct the message, such as querying a DB for some metadata that need to be added. If the log
     * level is not high enough at the moment, it doesn't make sense to execute the query.
     */
    getLevel(): number;
    /**
     * Sets the log level to the given value, preventing messages from less important log levels
     * from being printed to the console. Use in conjunction with the `log.LEVELS` constants such as
     *
     * ```
     * log.setLevel(log.LEVELS.DEBUG);
     * ```
     *
     * Default log level is INFO.
     */
    setLevel(level: LogLevel): void;
    internal(level: LogLevel, message: string, data?: any, exception?: any): void;
    /**
     * Configures logger.
     */
    setOptions(options: Partial<LoggerOptions>): void;
    /**
     * Returns the logger configuration.
     */
    getOptions(): Required<LoggerOptions>;
    /**
     * Creates a new instance of logger that inherits settings from a parent logger.
     */
    child(options: Partial<LoggerOptions>): Log;
    /**
     * Logs an `ERROR` message. Use this method to log error messages that are not directly connected
     * to an exception. For logging exceptions, use the `log.exception` method.
     */
    error(message: string, data?: AdditionalData): void;
    /**
     * Logs an `ERROR` level message with a nicely formatted exception. Note that the exception is the first parameter
     * here and an additional message is only optional.
     */
    exception(exception: Error, message: string, data?: AdditionalData): void;
    softFail(message: string, data?: AdditionalData): void;
    /**
     * Logs a `WARNING` level message. Data are stringified and appended to the message.
     */
    warning(message: string, data?: AdditionalData): void;
    /**
     * Logs an `INFO` message. `INFO` is the default log level so info messages will be always logged,
     * unless the log level is changed. Data are stringified and appended to the message.
     */
    info(message: string, data?: AdditionalData): void;
    /**
     * Logs a `DEBUG` message. By default, it will not be written to the console. To see `DEBUG`
     * messages in the console, set the log level to `DEBUG` either using the `log.setLevel(log.LEVELS.DEBUG)`
     * method or using the environment variable `APIFY_LOG_LEVEL=DEBUG`. Data are stringified and appended
     * to the message.
     */
    debug(message: string, data?: AdditionalData): void;
    perf(message: string, data?: AdditionalData): void;
    /**
     * Logs a `WARNING` level message only once.
     */
    warningOnce(message: string): void;
    /**
     * Logs given message only once as WARNING. It's used to warn user that some feature he is using has been deprecated.
     */
    deprecated(message: string): void;
}

/**
 * Ensures a string is shorter than a specified number of character, and truncates it if not, appending a specific suffix to it.
 * (copied from utilities package so logger do not have to depend on all of its dependencies)
 */
declare function truncate(str: string, maxLength: number, suffix?: string): string;
/**
 * Gets log level from env variable. Both integers and strings (WARNING) are supported.
 */
declare function getLevelFromEnv(): number;
/**
 * Gets log format from env variable. Currently, values 'JSON' and 'TEXT' are supported.
 * Defaults to 'TEXT' if no value is specified.
 */
declare function getFormatFromEnv(): LogFormat;
/**
 * Limits given object to given depth and escapes function with [function] string.
 *
 * ie. Replaces object's content by '[object]' and array's content
 * by '[array]' when the value is nested more than given limit.
 */
declare function limitDepth<T>(record: T, depth: number, maxStringLength?: number): T | undefined;
interface LimitedError {
    [IS_APIFY_LOGGER_EXCEPTION]: true;
    name: string;
    message: string;
    stack?: string;
    cause?: unknown;
    type?: string;
    details?: Record<string, unknown>;
    reason?: string;
    [key: string]: unknown;
}

declare class LoggerJson extends Logger {
    constructor(options?: {});
    _log(level: LogLevel, message: string, data?: any, exception?: unknown, opts?: Record<string, any>): string;
}

declare class LoggerText extends Logger {
    constructor(options?: {});
    _log(level: LogLevel, message: string, data?: any, exception?: unknown, opts?: Record<string, any>): string;
    protected _parseException(exception: unknown, indentLevel?: number): string;
    private _parseLoggerException;
}

declare const log: Log;

export { IS_APIFY_LOGGER_EXCEPTION, LEVELS, LEVEL_TO_STRING, type LimitedError, Log, LogFormat, LogLevel, Logger, LoggerJson, type LoggerOptions, LoggerText, PREFIX_DELIMITER, log as default, getFormatFromEnv, getLevelFromEnv, limitDepth, truncate };
