class Google::Cloud::Logging::Middleware

Constants

DEFAULT_LOG_NAME

The default log name used to instantiate the default logger if one isn't provided.

DEFAULT_LOG_NAME_MAP

A default value for the log_name_map argument. Directs health check logs to a separate log name so they don't spam the main log.

Attributes

logger[RW]

@private Global logger object

logger[R]

Public Class Methods

build_monitored_resource(type = nil, labels = nil) click to toggle source

Construct a monitored resource based on the given type and label if both are provided. Otherwise, construct a default monitored resource based on the current environment.

@param [String] type Type of Google::Cloud::Logging::Resource @param [Hash<String, String>] labels Metadata lebels of

Google::Cloud::Logging::Resource

@return [Google::Cloud::Logging::Resource] An Resource object with

type and labels

@see cloud.google.com/logging/docs/api/v2/resource-list

Monitored Resources and Services

@example If both type and labels are provided, it returns resource:

rc = Google::Cloud::Logging::Middleware.build_monitored_resource(
       "aws_ec2_instance",
       {
         instance_id: "ec2-id",
         aws_account: "aws-id"
       }
     )
rc.type   #=> "aws_ec2_instance"
rc.labels #=> { instance_id: "ec2-id", aws_account: "aws-id" }

@example If running from GAE, returns default resource:

rc = Google::Cloud::Logging::Middleware.build_monitored_resource
rc.type   #=> "gae_app"
rc.labels # { module_id: [GAE module name],
          #   version_id: [GAE module version] }

@example If running from GKE, returns default resource:

rc = Google::Cloud::Logging::Middleware.build_monitored_resource
rc.type   #=> "container"
rc.labels # { cluster_name: [GKE cluster name],
          #   namespace_id: [GKE namespace_id] }

@example If running from GCE, return default resource:

rc = Google::Cloud::Logging::Middleware.build_monitored_resource
rc.type   #=> "gce_instance"
rc.labels # { instance_id: [GCE VM instance id],
          #   zone: [GCE vm group zone] }

@example Otherwise default to generic “global” type:

rc = Google::Cloud::Logging::Middleware.build_monitored_resource
rc.type   #=> "global"
rc.labels #=> {}
# File lib/google/cloud/logging/middleware.rb, line 183
def self.build_monitored_resource type = nil, labels = nil
  if type && labels
    Google::Cloud::Logging::Resource.new.tap do |r|
      r.type = type
      r.labels = labels
    end
  else
    default_monitored_resource
  end
end
new(app, logger: nil, on_init: nil, **kwargs) click to toggle source

Create a new AppEngine logging Middleware.

@param [Rack Application] app Rack application @param [Google::Cloud::Logging::Logger] logger A logger to be used by

this middleware. The middleware will be interacting with the logger
to track Stackdriver request trace ID. It also properly sets
env["rack.logger"] to this assigned logger for accessing. If not
specified, a default logger with be used.

@param [Proc] on_init A callback to be invoked when the middleware is

initialized. The callback takes no arguments. Optional.

@param [Hash] kwargs Hash of configuration settings. Used for

backward API compatibility. See the [Configuration
Guide](https://googleapis.dev/ruby/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION.html)
for the prefered way to set configuration parameters.

@return [Google::Cloud::Logging::Middleware] A new

Google::Cloud::Logging::Middleware instance
# File lib/google/cloud/logging/middleware.rb, line 57
def initialize app, logger: nil, on_init: nil, **kwargs
  @app = app

  load_config(**kwargs)

  logger ||= Middleware.logger
  logger ||= begin
    log_name = configuration.log_name
    logging = Logging.new project_id:  configuration.project_id,
                          credentials: configuration.credentials
    resource = Middleware.build_monitored_resource(
      configuration.monitored_resource.type,
      configuration.monitored_resource.labels
    )
    Middleware.logger = logging.logger log_name, resource
  end

  on_init&.call

  @logger = logger
end

Private Class Methods

default_monitored_resource() click to toggle source

@private Extract information from current environment and construct the correct monitoring resource types and labels.

@return [Google::Cloud::Logging::Resource] An Resource object with

correct type and labels

@see cloud.google.com/logging/docs/api/v2/resource-list

Monitored Resources and Services

@example If running from GAE, returns default resource:

rc = Google::Cloud::Logging::Middleware.send \
       :default_monitored_resource
rc.type   #=> "gae_app"
rc.labels # { module_id: [GAE module name],
          #   version_id: [GAE module version] }

@example If running from GKE, returns default resource:

rc = Google::Cloud::Logging::Middleware.send \
       :default_monitored_resource
rc.type   #=> "container"
rc.labels # { cluster_name: [GKE cluster name],
          #   namespace_id: [GKE namespace_id] }

@example If running from GCE, return default resource:

rc = Google::Cloud::Logging::Middleware.send \
       :default_monitored_resource
rc.type   #=> "gce_instance"
rc.labels # { instance_id: [GCE VM instance id],
          #   zone: [GCE vm group zone] }

@example Otherwise default to generic “global” type:

rc = Google::Cloud::Logging::Middleware.send \
       :default_monitored_resource
rc.type   #=> "global"
rc.labels #=> {}
# File lib/google/cloud/logging/middleware.rb, line 237
def self.default_monitored_resource
  type, labels =
    if Google::Cloud.env.app_engine?
      ["gae_app", {
        module_id:  Google::Cloud.env.app_engine_service_id,
        version_id: Google::Cloud.env.app_engine_service_version
      }]
    elsif Google::Cloud.env.container_engine?
      namespace_id = Google::Cloud.env.container_engine_namespace_id
      namespace_id ||= "default"
      ["container", {
        cluster_name: Google::Cloud.env.container_engine_cluster_name,
        namespace_id: namespace_id
      }]
    elsif Google::Cloud.env.compute_engine?
      ["gce_instance", {
        instance_id: Google::Cloud.env.instance_name,
        zone:        Google::Cloud.env.instance_zone
      }]
    else
      ["global", {}]
    end

  Google::Cloud::Logging::Resource.new.tap do |r|
    r.type = type
    r.labels = labels
  end
end

Public Instance Methods

call(env) click to toggle source

Rack middleware entry point. In most Rack based frameworks, a request is served by one thread. So entry point, we associate the GCP request trace_id with the current thread's object_id in logger. All the logs written by logger beyond this point will carry this request's trace_id. Untrack the trace_id with this thread upon exiting.

@param [Hash] env Rack environment hash

@return [Rack::Response] The response from downstream Rack app

# File lib/google/cloud/logging/middleware.rb, line 90
def call env
  env["rack.logger"] = logger
  trace_id = get_trace_id env
  log_name = get_log_name env
  logger.add_request_info trace_id: trace_id, log_name: log_name,
                          env: env
  begin
    @app.call env
  ensure
    logger.delete_request_info
  end
end
get_log_name(env) click to toggle source

Determine the log name override for this request, if any.

@private @param [Hash] env The Rack environment. @return [String, nil] The log name override, or `nil` if there is

no override.
# File lib/google/cloud/logging/middleware.rb, line 123
def get_log_name env
  log_name_map = configuration.log_name_map
  return nil unless log_name_map
  path = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
  path = "/#{path}" unless path.start_with? "/"
  log_name_map.each do |k, v|
    return v if k === path
  end
  nil
end
get_trace_id(env) click to toggle source

Determine the trace ID for this request.

@private @param [Hash] env The Rack environment. @return [String] The trace ID.

# File lib/google/cloud/logging/middleware.rb, line 110
def get_trace_id env
  trace_context = Stackdriver::Core::TraceContext.parse_rack_env env
  trace_context.trace_id
end

Private Instance Methods

configuration() click to toggle source

@private Get Google::Cloud::Logging.configure

# File lib/google/cloud/logging/middleware.rb, line 299
def configuration
  Google::Cloud::Logging.configure
end
init_default_config() click to toggle source

Fallback to default configuration values if not defined already

# File lib/google/cloud/logging/middleware.rb, line 290
def init_default_config
  configuration.project_id ||= (Cloud.configure.project_id || Logging.default_project_id)
  configuration.credentials ||= Cloud.configure.credentials
  configuration.log_name ||= DEFAULT_LOG_NAME
  configuration.log_name_map ||= DEFAULT_LOG_NAME_MAP
end
load_config(**kwargs) click to toggle source

Consolidate configurations from various sources. Also set instrumentation config parameters to default values if not set already.

# File lib/google/cloud/logging/middleware.rb, line 275
def load_config **kwargs
  project_id = kwargs[:project] || kwargs[:project_id]
  configuration.project_id = project_id unless project_id.nil?

  creds = kwargs[:credentials] || kwargs[:keyfile]
  configuration.credentials = creds unless creds.nil?

  log_name_map = kwargs[:log_name_map]
  configuration.log_name_map = log_name_map unless log_name_map.nil?

  init_default_config
end