"use strict";
/*!
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.v2 = exports.protos = exports.Logging = exports.Sink = exports.LogSync = exports.formatLogName = exports.assignSeverityToEntries = exports.Severity = exports.Log = exports.Entry = exports.detectServiceContext = exports.middleware = void 0;
const common_1 = require("@google-cloud/common");
const paginator_1 = require("@google-cloud/paginator");
const projectify_1 = require("@google-cloud/projectify");
const promisify_1 = require("@google-cloud/promisify");
const arrify = require("arrify");
const extend = require("extend");
const gax = require("google-gax");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pumpify = require('pumpify');
const streamEvents = require("stream-events");
const middleware = require("./middleware");
exports.middleware = middleware;
const metadata_1 = require("./utils/metadata");
Object.defineProperty(exports, "detectServiceContext", { enumerable: true, get: function () { return metadata_1.detectServiceContext; } });
const version = require('../../package.json').version;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const v2 = require('./v2');
exports.v2 = v2;
const entry_1 = require("./entry");
Object.defineProperty(exports, "Entry", { enumerable: true, get: function () { return entry_1.Entry; } });
const log_common_1 = require("./utils/log-common");
Object.defineProperty(exports, "Severity", { enumerable: true, get: function () { return log_common_1.Severity; } });
Object.defineProperty(exports, "formatLogName", { enumerable: true, get: function () { return log_common_1.formatLogName; } });
Object.defineProperty(exports, "assignSeverityToEntries", { enumerable: true, get: function () { return log_common_1.assignSeverityToEntries; } });
const log_1 = require("./log");
Object.defineProperty(exports, "Log", { enumerable: true, get: function () { return log_1.Log; } });
const log_sync_1 = require("./log-sync");
Object.defineProperty(exports, "LogSync", { enumerable: true, get: function () { return log_sync_1.LogSync; } });
const sink_1 = require("./sink");
Object.defineProperty(exports, "Sink", { enumerable: true, get: function () { return sink_1.Sink; } });
const stream_1 = require("stream");
/**
 * @typedef {object} ClientConfig
 * @property {string} [projectId] The project ID from the Google Developer's
 *     Console, e.g. 'grape-spaceship-123'. We will also check the environment
 *     variable `GCLOUD_PROJECT` for your project ID. If your app is running in
 *     an environment which supports {@link
 * https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
 * Application Default Credentials}, your project ID will be detected
 * automatically.
 * @property {string} [keyFilename] Full path to the a .json, .pem, or .p12 key
 *     downloaded from the Google Developers Console. If you provide a path to a
 *     JSON file, the `projectId` option above is not necessary. NOTE: .pem and
 *     .p12 require you to specify the `email` option as well.
 * @property {string} [email] Account email address. Required when using a .pem
 *     or .p12 keyFilename.
 * @property {object} [credentials] Credentials object.
 * @property {string} [credentials.client_email]
 * @property {string} [credentials.private_key]
 * @property {boolean} [autoRetry=true] Automatically retry requests if the
 *     response is related to rate limits or certain intermittent server errors.
 *     We will exponentially backoff subsequent requests by default.
 * @property {number} [maxRetries=3] Maximum number of automatic retries
 *     attempted before returning the error.
 * @property {Constructor} [promise] Custom promise module to use instead of
 *     native Promises.
 */
/**
 * {@link https://cloud.google.com/logging/docs| Cloud Logging} allows you to
 * store, search, analyze, monitor, and alert on log data and events from Google
 * Cloud Platform and Amazon Web Services (AWS).
 *
 * @class
 *
 * See {@link https://cloud.google.com/logging/docs| What is Cloud Logging?}
 *
 * See {@link https://cloud.google.com/logging/docs/api| Introduction to the Cloud Logging API}
 *
 * See {@link https://www.npmjs.com/package/@google-cloud/logging-bunyan| Logging to Google Cloud from Bunyan}
 *
 * See {@link https://www.npmjs.com/package/@google-cloud/logging-winston| Logging to Google Cloud from Winston}
 *
 * @param {ClientConfig} [options] Configuration options.
 *
 * @example Import the client library
 * ```
 * const {Logging} = require('@google-cloud/logging');
 *
 * ```
 * @example Create a client that uses <a href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application Default Credentials (ADC)</a>:
 * ```
 * const logging = new Logging();
 *
 * ```
 * @example Create a client with <a href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicitcredentials</a>:
 * ```
 * const logging = new Logging({ projectId:
 *  'your-project-id', keyFilename: '/path/to/keyfile.json'
 * });
 *
 * ```
 * @example <caption>include:samples/quickstart.js</caption>
 * region_tag:logging_quickstart
 * Full quickstart example:
 */
class Logging {
    constructor(options) {
        // Determine what scopes are needed.
        // It is the union of the scopes on all three clients.
        const scopes = [];
        const clientClasses = [
            v2.ConfigServiceV2Client,
            v2.LoggingServiceV2Client,
            v2.MetricsServiceV2Client,
        ];
        for (const clientClass of clientClasses) {
            for (const scope of clientClass.scopes) {
                if (scopes.indexOf(scope) === -1) {
                    scopes.push(scope);
                }
            }
        }
        const options_ = extend({
            libName: 'gccl',
            libVersion: version,
            scopes,
        }, options);
        this.api = {};
        this.auth = new gax.GoogleAuth(options_);
        this.options = options_;
        this.projectId = this.options.projectId || '{{projectId}}';
        this.configService = new v2.ConfigServiceV2Client(this.options);
        this.loggingService = new v2.LoggingServiceV2Client(this.options);
    }
    // jscs:disable maximumLineLength
    /**
     * Create a sink.
     *
     * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks|Sink Overview}
     * See {@link https://cloud.google.com/logging/docs/view/advanced_filters|Advanced Logs Filters}
     * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/create|projects.sinks.create API Documentation}
     *
     * @param {string} name Name of the sink.
     * @param {CreateSinkRequest} config Config to set for the sink.
     * @param {CreateSinkCallback} [callback] Callback function.
     * @returns {Promise<CreateSinkResponse>}
     * @throws {Error} If a name is not provided.
     * @throws {Error} if a config object is not provided.
     * @see Sink#create
     *
     * @example
     * ```
     * const {Storage} = require('@google-cloud/storage');
     * const storage = new Storage({
     *   projectId: 'grape-spaceship-123'
     * });
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * const config = {
     *   destination: storage.bucket('logging-bucket'),
     *   filter: 'severity = ALERT'
     * };
     *
     * function callback(err, sink, apiResponse) {
     *   // `sink` is a Sink object.
     * }
     *
     * logging.createSink('new-sink-name', config, callback);
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * logging.createSink('new-sink-name', config).then(data => {
     *   const sink = data[0];
     *   const apiResponse = data[1];
     * });
     *
     * ```
     * @example <caption>include:samples/sinks.js</caption>
     * region_tag:logging_create_sink
     * Another example:
     */
    async createSink(name, config) {
        if (typeof name !== 'string') {
            throw new Error('A sink name must be provided.');
        }
        if (typeof config !== 'object') {
            throw new Error('A sink configuration object must be provided.');
        }
        if (common_1.util.isCustomType(config.destination, 'bigquery/dataset')) {
            await this.setAclForDataset_(config);
        }
        if (common_1.util.isCustomType(config.destination, 'pubsub/topic')) {
            await this.setAclForTopic_(config);
        }
        if (common_1.util.isCustomType(config.destination, 'storage/bucket')) {
            await this.setAclForBucket_(config);
        }
        const reqOpts = {
            parent: 'projects/' + this.projectId,
            sink: extend({}, config, { name }),
            uniqueWriterIdentity: config.uniqueWriterIdentity,
        };
        delete reqOpts.sink.gaxOptions;
        delete reqOpts.sink.uniqueWriterIdentity;
        await this.setProjectId(reqOpts);
        const [resp] = await this.configService.createSink(reqOpts, config.gaxOptions);
        const sink = this.sink(resp.name);
        sink.metadata = resp;
        return [sink, resp];
    }
    /**
     * Create an entry object.
     *
     * Using this method will not itself make any API requests. You will use
     * the object returned in other API calls, such as
     * {@link Log#write}.
     *
     * Note, {@link https://cloud.google.com/logging/quotas|Cloud Logging Quotas and limits}
     * dictates that the maximum log entry size, including all
     * [LogEntry Resource properties]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry},
     * cannot exceed _approximately_ 256 KB.
     *
     * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry|LogEntry JSON representation}
     *
     * @param {?object|?string} [resource] See a
     *     [Monitored
     * Resource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource).
     * @param {object|string} data The data to use as the value for this log
     *     entry.
     * @returns {Entry}
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * const resource = {
     *   type: 'gce_instance',
     *   labels: {
     *     zone: 'global',
     *     instance_id: '3'
     *   }
     * };
     *
     * const entry = logging.entry(resource, {
     *   delegate: 'my_username'
     * });
     *
     * entry.toJSON();
     * // {
     * //   resource: {
     * //     type: 'gce_instance',
     * //     labels: {
     * //       zone: 'global',
     * //       instance_id: '3'
     * //     }
     * //   },
     * //   jsonPayload: {
     * //     delegate: 'my_username'
     * //   }
     * // }
     * ```
     */
    entry(resource, data) {
        return new entry_1.Entry(resource, data);
    }
    async getEntries(opts) {
        const options = opts ? opts : {};
        // By default, sort entries by descending timestamp
        let reqOpts = extend({ orderBy: 'timestamp desc' }, options);
        // By default, filter entries to last 24 hours only
        const time = new Date();
        time.setDate(time.getDate() - 1);
        const timeFilter = `timestamp >= "${time.toISOString()}"`;
        if (!options.filter) {
            reqOpts = extend({ filter: timeFilter }, reqOpts);
        }
        else if (!options.filter.includes('timestamp')) {
            reqOpts.filter += ` AND ${timeFilter}`;
        }
        reqOpts.resourceNames = arrify(reqOpts.resourceNames);
        this.projectId = await this.auth.getProjectId();
        const resourceName = 'projects/' + this.projectId;
        if (reqOpts.resourceNames.indexOf(resourceName) === -1) {
            reqOpts.resourceNames.push(resourceName);
        }
        delete reqOpts.autoPaginate;
        delete reqOpts.gaxOptions;
        const gaxOptions = extend({
            autoPaginate: options.autoPaginate,
        }, options.gaxOptions);
        const resp = await this.loggingService.listLogEntries(reqOpts, gaxOptions);
        const [entries] = resp;
        if (entries) {
            resp[0] = entries.map(entry_1.Entry.fromApiResponse_);
        }
        return resp;
    }
    /**
     * List the {@link Entry} objects in your logs as a readable object
     * stream.
     *
     * @method Logging#getEntriesStream
     * @param {GetEntriesRequest} [query] Query object for listing entries.
     * @returns {ReadableStream} A readable stream that emits {@link Entry}
     *     instances.
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * logging.getEntriesStream()
     *   .on('error', console.error)
     *   .on('data', entry => {
     *     // `entry` is a Cloud Logging entry object.
     *     // See the `data` property to read the data from the entry.
     *   })
     *   .on('end', function() {
     *     // All entries retrieved.
     *   });
     *
     * //-
     * // If you anticipate many results, you can end a stream early to prevent
     * // unnecessary processing and API requests.
     * //-
     * logging.getEntriesStream()
     *   .on('data', function(entry) {
     *     this.end();
     *   });
     * ```
     */
    getEntriesStream(options = {}) {
        let requestStream;
        const userStream = streamEvents(pumpify.obj());
        userStream.abort = () => {
            if (requestStream) {
                requestStream.abort();
            }
        };
        const toEntryStream = new stream_1.Transform({
            objectMode: true,
            transform: (chunk, encoding, callback) => {
                callback(null, entry_1.Entry.fromApiResponse_(chunk));
            },
        });
        userStream.once('reading', () => {
            this.auth.getProjectId().then(projectId => {
                this.projectId = projectId;
                if (options.log) {
                    if (options.filter) {
                        options.filter = `(${options.filter}) AND logName="${log_common_1.formatLogName(this.projectId, options.log)}"`;
                    }
                    else {
                        options.filter = `logName="${log_common_1.formatLogName(this.projectId, options.log)}"`;
                    }
                    delete options.log;
                }
                const reqOpts = extend({
                    orderBy: 'timestamp desc',
                }, options);
                reqOpts.resourceNames = arrify(reqOpts.resourceNames);
                reqOpts.resourceNames.push(`projects/${this.projectId}`);
                delete reqOpts.autoPaginate;
                delete reqOpts.gaxOptions;
                const gaxOptions = extend({
                    autoPaginate: options.autoPaginate,
                }, options.gaxOptions);
                let gaxStream;
                requestStream = streamEvents(new stream_1.PassThrough({ objectMode: true }));
                requestStream.abort = () => {
                    if (gaxStream && gaxStream.cancel) {
                        gaxStream.cancel();
                    }
                };
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                if (!global.GCLOUD_SANDBOX_ENV) {
                    requestStream.once('reading', () => {
                        try {
                            gaxStream = this.loggingService.listLogEntriesStream(reqOpts, gaxOptions);
                        }
                        catch (error) {
                            requestStream.destroy(error);
                            return;
                        }
                        gaxStream
                            .on('error', err => {
                            requestStream.destroy(err);
                        })
                            .pipe(requestStream);
                        return;
                    });
                }
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                userStream.setPipeline(requestStream, toEntryStream);
            });
        });
        return userStream;
    }
    /**
     * Query object for streaming entries.
     *
     * @typedef {object} TailEntriesRequest
     * @property {Array.<string>|string} [resourceNames] Names of project
     *     resources to stream logs out of.
     * @property {string} [filter] An
     *     [advanced logs
     * filter](https://cloud.google.com/logging/docs/view/advanced_filters). An
     * empty filter matches all log entries.
     * @property {number} [bufferWindow=2] A setting to balance the tradeoff
     *     between viewing the log entries as they are being written and viewing
     *     them in ascending order.
     * @property {string} [log] A name of the log specifying to only return
     *     entries from this log.
     * @property {object} [gaxOptions] Request configuration options, outlined
     *     here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions.
     */
    /**
     * Streaming read of live logs as log entries are ingested. Until the stream
     * is terminated, it will continue reading logs.
     *
     * @method Logging#tailEntries
     * @param {TailEntriesRequest} [query] Query object for tailing entries.
     * @returns {DuplexStream} A duplex stream that emits TailEntriesResponses
     * containing an array of {@link Entry} instances.
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * logging.tailEntries()
     *   .on('error', console.error)
     *   .on('data', resp => {
     *     console.log(resp.entries);
     *     console.log(resp.suppressionInfo);
     *   })
     *   .on('end', function() {
     *     // All entries retrieved.
     *   });
     *
     * //-
     * // If you anticipate many results, you can end a stream early to prevent
     * // unnecessary processing and API requests.
     * //-
     * logging.getEntriesStream()
     *   .on('data', function(entry) {
     *     this.end();
     *   });
     * ```
     */
    tailEntries(options = {}) {
        const userStream = streamEvents(pumpify.obj());
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let gaxStream;
        userStream.abort = () => {
            if (gaxStream && gaxStream.cancel) {
                gaxStream.cancel();
            }
        };
        const transformStream = new stream_1.Transform({
            objectMode: true,
            transform: (chunk, encoding, callback) => {
                callback(null, (() => {
                    const formattedEntries = [];
                    chunk.entries.forEach((entry) => {
                        formattedEntries.push(entry_1.Entry.fromApiResponse_(entry));
                    });
                    const resp = {
                        entries: formattedEntries,
                        suppressionInfo: chunk.suppressionInfo,
                    };
                    return resp;
                })());
            },
        });
        this.auth.getProjectId().then(projectId => {
            this.projectId = projectId;
            if (options.log) {
                if (options.filter) {
                    options.filter = `(${options.filter}) AND logName="${log_common_1.formatLogName(this.projectId, options.log)}"`;
                }
                else {
                    options.filter = `logName="${log_common_1.formatLogName(this.projectId, options.log)}"`;
                }
            }
            options.resourceNames = arrify(options.resourceNames);
            options.resourceNames.push(`projects/${this.projectId}`);
            const writeOptions = {
                resourceNames: options.resourceNames,
                ...(options.filter && { filter: options.filter }),
                ...(options.bufferWindow && { bufferwindow: options.bufferWindow }),
            };
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            if (!global.GCLOUD_SANDBOX_ENV) {
                gaxStream = this.loggingService.tailLogEntries(options.gaxOptions);
                // Write can only be called once in a single tail streaming session.
                gaxStream.write(writeOptions);
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                userStream.setPipeline(gaxStream, transformStream);
            }
        });
        return userStream;
    }
    async getLogs(opts) {
        const options = opts ? opts : {};
        this.projectId = await this.auth.getProjectId();
        const reqOpts = extend({}, options, {
            parent: 'projects/' + this.projectId,
        });
        delete reqOpts.autoPaginate;
        delete reqOpts.gaxOptions;
        const gaxOptions = extend({
            autoPaginate: options.autoPaginate,
        }, options.gaxOptions);
        const resp = await this.loggingService.listLogs(reqOpts, gaxOptions);
        const [logs] = resp;
        if (logs) {
            resp[0] = logs.map((logName) => this.log(logName));
        }
        return resp;
    }
    /**
     * List the {@link Log} objects in your project as a readable object stream.
     *
     * @method Logging#getLogsStream
     * @param {GetLogsRequest} [query] Query object for listing entries.
     * @returns {ReadableStream} A readable stream that emits {@link Log}
     *     instances.
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * logging.getLogsStream()
     *   .on('error', console.error)
     *   .on('data', log => {
     *     // `log` is a Cloud Logging log object.
     *   })
     *   .on('end', function() {
     *     // All logs retrieved.
     *   });
     *
     * //-
     * // If you anticipate many results, you can end a stream early to prevent
     * // unnecessary processing and API requests.
     * //-
     * logging.getLogsStream()
     *   .on('data', log => {
     *     this.end();
     *   });
     * ```
     */
    getLogsStream(options = {}) {
        options = options || {};
        let requestStream;
        const userStream = streamEvents(pumpify.obj());
        userStream.abort = () => {
            if (requestStream) {
                requestStream.abort();
            }
        };
        const toLogStream = new stream_1.Transform({
            objectMode: true,
            transform: (chunk, encoding, callback) => {
                callback(null, this.log(chunk));
            },
        });
        userStream.once('reading', () => {
            this.auth.getProjectId().then(projectId => {
                this.projectId = projectId;
                const reqOpts = extend({}, options, {
                    parent: 'projects/' + this.projectId,
                });
                delete reqOpts.gaxOptions;
                const gaxOptions = extend({
                    autoPaginate: options.autoPaginate,
                }, options.gaxOptions);
                let gaxStream;
                requestStream = streamEvents(new stream_1.PassThrough({ objectMode: true }));
                requestStream.abort = () => {
                    if (gaxStream && gaxStream.cancel) {
                        gaxStream.cancel();
                    }
                };
                requestStream.once('reading', () => {
                    try {
                        gaxStream = this.loggingService.listLogsStream(reqOpts, gaxOptions);
                    }
                    catch (error) {
                        requestStream.destroy(error);
                        return;
                    }
                    gaxStream
                        .on('error', err => {
                        requestStream.destroy(err);
                    })
                        .pipe(requestStream);
                    return;
                });
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                userStream.setPipeline(requestStream, toLogStream);
            });
        });
        return userStream;
    }
    async getSinks(opts) {
        const options = opts ? opts : {};
        this.projectId = await this.auth.getProjectId();
        const reqOpts = extend({}, options, {
            parent: 'projects/' + this.projectId,
        });
        delete reqOpts.autoPaginate;
        delete reqOpts.gaxOptions;
        const gaxOptions = extend({
            autoPaginate: options.autoPaginate,
        }, options.gaxOptions);
        const resp = await this.configService.listSinks(reqOpts, gaxOptions);
        const [sinks] = resp;
        if (sinks) {
            resp[0] = sinks.map((sink) => {
                const sinkInstance = this.sink(sink.name);
                sinkInstance.metadata = sink;
                return sinkInstance;
            });
        }
        return resp;
    }
    /**
     * Get the {@link Sink} objects associated with this project as a
     * readable object stream.
     *
     * @method Logging#getSinksStream
     * @param {GetSinksRequest} [query] Query object for listing sinks.
     * @returns {ReadableStream} A readable stream that emits {@link Sink}
     *     instances.
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * logging.getSinksStream()
     *   .on('error', console.error)
     *   .on('data', sink => {
     *     // `sink` is a Sink object.
     *   })
     *   .on('end', function() {
     *     // All sinks retrieved.
     *   });
     *
     * //-
     * // If you anticipate many results, you can end a stream early to prevent
     * // unnecessary processing and API requests.
     * //-
     * logging.getSinksStream()
     *   .on('data', function(sink) {
     *     this.end();
     *   });
     * ```
     */
    getSinksStream(options) {
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const self = this;
        options = options || {};
        let requestStream;
        const userStream = streamEvents(pumpify.obj());
        userStream.abort = () => {
            if (requestStream) {
                requestStream.abort();
            }
        };
        const toSinkStream = new stream_1.Transform({
            objectMode: true,
            transform: (chunk, encoding, callback) => {
                const sinkInstance = self.sink(chunk.name);
                sinkInstance.metadata = chunk;
                callback(null, sinkInstance);
            },
        });
        userStream.once('reading', () => {
            this.auth.getProjectId().then(projectId => {
                this.projectId = projectId;
                const reqOpts = extend({}, options, {
                    parent: 'projects/' + self.projectId,
                });
                delete reqOpts.gaxOptions;
                const gaxOptions = extend({
                    autoPaginate: options.autoPaginate,
                }, options.gaxOptions);
                let gaxStream;
                requestStream = streamEvents(new stream_1.PassThrough({ objectMode: true }));
                requestStream.abort = () => {
                    if (gaxStream && gaxStream.cancel) {
                        gaxStream.cancel();
                    }
                };
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                if (!global.GCLOUD_SANDBOX_ENV) {
                    requestStream.once('reading', () => {
                        try {
                            gaxStream = this.configService.listSinksStream(reqOpts, gaxOptions);
                        }
                        catch (error) {
                            requestStream.destroy(error);
                            return;
                        }
                        gaxStream
                            .on('error', err => {
                            requestStream.destroy(err);
                        })
                            .pipe(requestStream);
                        return;
                    });
                }
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                userStream.setPipeline(requestStream, toSinkStream);
            });
        });
        return userStream;
    }
    /**
     * Get a reference to a Cloud Logging log.
     *
     * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.logs|Log Overview}
     *
     * @param {string} name Name of the existing log.
     * @param {object} [options] Configuration object.
     * @param {boolean} [options.removeCircular] Replace circular references in
     *     logged objects with a string value, `[Circular]`. (Default: false)
     * @returns {Log}
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     * const log = logging.log('my-log');
     * ```
     */
    log(name, options) {
        return new log_1.Log(this, name, options);
    }
    /**
     * Get a reference to a Cloud Logging logSync.
     *
     * @param {string} name Name of the existing log.
     * @param {object} transport An optional write stream.
     * @returns {LogSync}
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     *
     * // Optional: enrich logs with additional context
     * await logging.setProjectId();
     * await logging.setDetectedResource();
     *
     * // Default transport writes to process.stdout
     * const log = logging.logSync('my-log');
     * ```
     */
    logSync(name, transport) {
        return new log_sync_1.LogSync(this, name, transport);
    }
    /**
     * Get a reference to a Cloud Logging sink.
     *
     * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks|Sink Overview}
     *
     * @param {string} name Name of the existing sink.
     * @returns {Sink}
     *
     * @example
     * ```
     * const {Logging} = require('@google-cloud/logging');
     * const logging = new Logging();
     * const sink = logging.sink('my-sink');
     * ```
     */
    sink(name) {
        return new sink_1.Sink(this, name);
    }
    /**
     * Funnel all API requests through this method, to be sure we have a project
     * ID.
     *
     * @param {object} config Configuration object.
     * @param {object} config.gaxOpts GAX options.
     * @param {function} config.method The gax method to call.
     * @param {object} config.reqOpts Request options.
     * @param {function} [callback] Callback function.
     */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    request(config, callback) {
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const self = this;
        const isStreamMode = !callback;
        let gaxStream;
        let stream;
        if (isStreamMode) {
            stream = streamEvents(new stream_1.PassThrough({ objectMode: true }));
            stream.abort = () => {
                if (gaxStream && gaxStream.cancel) {
                    gaxStream.cancel();
                }
            };
            stream.once('reading', makeRequestStream);
        }
        else {
            makeRequestCallback();
        }
        function prepareGaxRequest(callback) {
            self.auth.getProjectId((err, projectId) => {
                if (err) {
                    callback(err);
                    return;
                }
                self.projectId = projectId;
                let gaxClient = self.api[config.client];
                if (!gaxClient) {
                    // Lazily instantiate client.
                    gaxClient = new v2[config.client](self.options);
                    self.api[config.client] = gaxClient;
                }
                let reqOpts = extend(true, {}, config.reqOpts);
                reqOpts = projectify_1.replaceProjectIdToken(reqOpts, projectId);
                const requestFn = gaxClient[config.method].bind(gaxClient, reqOpts, config.gaxOpts);
                callback(null, requestFn);
            });
        }
        function makeRequestCallback() {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            if (global.GCLOUD_SANDBOX_ENV) {
                return;
            }
            prepareGaxRequest((err, requestFn) => {
                if (err) {
                    callback(err);
                    return;
                }
                requestFn(callback);
            });
        }
        function makeRequestStream() {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            if (global.GCLOUD_SANDBOX_ENV) {
                return new stream_1.PassThrough({ objectMode: true });
            }
            prepareGaxRequest((err, requestFn) => {
                if (err) {
                    stream.destroy(err);
                    return;
                }
                gaxStream = requestFn();
                gaxStream
                    .on('error', err => {
                    stream.destroy(err);
                })
                    .pipe(stream);
            });
            return;
        }
        return stream;
    }
    /**
     * This method is called when creating a sink with a Bucket destination. The
     * bucket must first grant proper ACL access to the Cloud Logging
     * account.
     *
     * The parameters are the same as what {@link Logging#createSink} accepts.
     *
     * @private
     */
    async setAclForBucket_(config) {
        const bucket = config.destination;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await bucket.acl.owners.addGroup('cloud-logs@google.com');
        config.destination = 'storage.googleapis.com/' + bucket.name;
    }
    /**
     * This method is called when creating a sink with a Dataset destination. The
     * dataset must first grant proper ACL access to the Cloud Logging
     * account.
     *
     * The parameters are the same as what {@link Logging#createSink} accepts.
     *
     * @private
     */
    async setAclForDataset_(config) {
        const dataset = config.destination;
        const [metadata] = await dataset.getMetadata();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const access = [].slice.call(arrify(metadata.access));
        access.push({
            role: 'WRITER',
            groupByEmail: 'cloud-logs@google.com',
        });
        await dataset.setMetadata({
            access,
        });
        const baseUrl = 'bigquery.googleapis.com';
        const pId = dataset.parent.projectId;
        const dId = dataset.id;
        config.destination = `${baseUrl}/projects/${pId}/datasets/${dId}`;
    }
    /**
     * This method is called when creating a sink with a Topic destination. The
     * topic must first grant proper ACL access to the Cloud Logging
     * account.
     *
     * The parameters are the same as what {@link Logging#createSink} accepts.
     *
     * @private
     */
    async setAclForTopic_(config) {
        const topic = config.destination;
        const [policy] = await topic.iam.getPolicy();
        policy.bindings = arrify(policy.bindings);
        policy.bindings.push({
            role: 'roles/pubsub.publisher',
            members: ['serviceAccount:cloud-logs@system.gserviceaccount.com'],
        });
        await topic.iam.setPolicy(policy);
        const baseUrl = 'pubsub.googleapis.com';
        const topicName = topic.name;
        config.destination = `${baseUrl}/${topicName}`;
    }
    /**
     * setProjectId detects and sets a projectId string on the Logging instance.
     * It can be invoked once to ensure ensuing LogSync entries have a projectID.
     * @param reqOpts
     */
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    async setProjectId(reqOpts) {
        if (this.projectId === '{{projectId}}')
            this.projectId = await this.auth.getProjectId();
        if (reqOpts)
            reqOpts = projectify_1.replaceProjectIdToken(reqOpts, this.projectId);
    }
    /**
     * setResource detects and sets a detectedresource object on the Logging
     * instance. It can be invoked once to ensure ensuing LogSync entries contain
     * resource context.
     */
    async setDetectedResource() {
        if (!this.detectedResource) {
            this.detectedResource = await metadata_1.getDefaultResource(this.auth);
        }
    }
}
exports.Logging = Logging;
/*! Developer Documentation
 * All async methods (except for streams) will execute a callback in the event
 * that a callback is provided.
 */
promisify_1.callbackifyAll(Logging, {
    exclude: ['request'],
});
/*! Developer Documentation
 *
 * These methods can be auto-paginated.
 */
paginator_1.paginator.extend(Logging, ['getEntries', 'getLogs', 'getSinks']);
/**
 * Reference to the low-level auto-generated clients for the V2 Logging service.
 *
 * @type {object}
 * @property {constructor} LoggingServiceV2Client
 *   Reference to {@link v2.LoggingServiceV2Client}
 * @property {constructor} ConfigServiceV2Client
 *   Reference to {@link v2.ConfigServiceV2Client}
 * @property {constructor} MetricsServiceV2Client
 *   Reference to {@link v2.MetricsServiceV2Client}
 */
module.exports.v2 = v2;
const protos = require("../protos/protos");
exports.protos = protos;
//# sourceMappingURL=index.js.map