gooddata 1.3.6-java → 2.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +54 -36
- data/CHANGELOG.md +37 -1
- data/CONTRIBUTING.md +4 -0
- data/Gemfile +9 -0
- data/Jenkinsfile-chart +14 -0
- data/RELEASING.md +5 -8
- data/Rakefile +1 -1
- data/SDK_VERSION +1 -1
- data/bin/run_brick.rb +1 -1
- data/bin/test_projects_cleanup.rb +20 -8
- data/ci.rake +2 -2
- data/k8s/charts/lcm-bricks/Chart.yaml +4 -0
- data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +96 -0
- data/lcm.rake +1 -1
- data/lib/gooddata/bricks/middleware/context_logger_decorator.rb +31 -0
- data/lib/gooddata/bricks/middleware/context_manager.rb +68 -0
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +15 -9
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +20 -0
- data/lib/gooddata/bricks/middleware/mask_logger_decorator.rb +35 -5
- data/lib/gooddata/client.rb +2 -3
- data/lib/gooddata/core/gd_logger.rb +92 -0
- data/lib/gooddata/core/logging.rb +24 -7
- data/lib/gooddata/core/nil_logger.rb +1 -2
- data/lib/gooddata/core/splunk_logger.rb +23 -0
- data/lib/gooddata/lcm/actions/synchronize_users.rb +2 -1
- data/lib/gooddata/lcm/helpers/check_helper.rb +3 -15
- data/lib/gooddata/lcm/helpers/safe_failure_helper.rb +19 -0
- data/lib/gooddata/lcm/lcm2.rb +21 -10
- data/lib/gooddata/mixins/property_accessor.rb +30 -0
- data/lib/gooddata/models/execution.rb +5 -0
- data/lib/gooddata/models/project.rb +6 -4
- data/lib/gooddata/rest/client.rb +17 -6
- data/lib/gooddata/rest/connection.rb +20 -6
- data/lib/gooddata/rest/rest_aggregator.rb +46 -0
- metadata +12 -2
@@ -0,0 +1,68 @@
|
|
1
|
+
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
|
2
|
+
# This source code is licensed under the BSD-style license found in the
|
3
|
+
# LICENSE file in the root directory of this source tree.
|
4
|
+
|
5
|
+
require_relative '../../mixins/property_accessor'
|
6
|
+
require_relative '../../lcm/helpers/helpers'
|
7
|
+
|
8
|
+
module GoodData
|
9
|
+
module ContextManager
|
10
|
+
extend GoodData::Mixin::PropertyAccessor
|
11
|
+
|
12
|
+
property_accessor :@context, :action
|
13
|
+
property_accessor :@context, :brick
|
14
|
+
property_accessor :@context, :execution_id
|
15
|
+
property_accessor :@context, :status
|
16
|
+
|
17
|
+
def initialize_context
|
18
|
+
@action_start = Time.now
|
19
|
+
|
20
|
+
# :log_v is used to differentiate new versions of logs in splunk
|
21
|
+
@context = {
|
22
|
+
:api_version => GoodData.version,
|
23
|
+
:log_v => 0,
|
24
|
+
:component => 'lcm.ruby',
|
25
|
+
:status => :not_in_action
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return current brick context extended with time specific information
|
30
|
+
#
|
31
|
+
# @param [Time] now, allows to specify exact time, when outer call was performed
|
32
|
+
# @return [Hash] Brick context
|
33
|
+
def context(now = Time.now)
|
34
|
+
time_specific_context = action ? { :time => time_from_action_start(now) } : {}
|
35
|
+
@context.merge(time_specific_context)
|
36
|
+
end
|
37
|
+
|
38
|
+
def time_from_action_start(now = Time.now)
|
39
|
+
fail_if_development 'No action is being profiled' unless action
|
40
|
+
(now - @action_start) * 1000
|
41
|
+
end
|
42
|
+
|
43
|
+
# Starts lcm action
|
44
|
+
#
|
45
|
+
# @param [String] action, name of the action
|
46
|
+
# @param [Logger] logger, logger that should log current context info
|
47
|
+
# @param [Time] now, allows to specify exact time, when outer call was performed
|
48
|
+
def start_action(next_action, logger = nil, now = Time.now)
|
49
|
+
fail_if_development 'An action is already being profiled' if action
|
50
|
+
|
51
|
+
self.action = next_action
|
52
|
+
@action_start = now
|
53
|
+
logger.info '' if logger
|
54
|
+
self.status = :action_in_progress
|
55
|
+
end
|
56
|
+
|
57
|
+
# Ends currently opened lcm action
|
58
|
+
#
|
59
|
+
# @param [Logger] logger, logger that should log current context info
|
60
|
+
def end_action(logger = nil)
|
61
|
+
fail_if_development 'No matching action to start found' unless action
|
62
|
+
|
63
|
+
logger.info '' if logger
|
64
|
+
self.status = :not_in_action
|
65
|
+
self.action = nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -26,6 +26,9 @@ module GoodData
|
|
26
26
|
DEFAULT_HOSTNAME = 'secure.gooddata.com'
|
27
27
|
|
28
28
|
def call(params)
|
29
|
+
# Generate brick execution id
|
30
|
+
execution_id = GoodData.gd_logger.execution_id
|
31
|
+
|
29
32
|
# Convert possible jruby hash to plain hash
|
30
33
|
params = params.to_hash
|
31
34
|
|
@@ -48,7 +51,8 @@ module GoodData
|
|
48
51
|
params['GDC_VERIFY_SSL'].to_b,
|
49
52
|
params['GDC_USERNAME'],
|
50
53
|
params['GDC_PASSWORD'],
|
51
|
-
params['GDC_SST']
|
54
|
+
params['GDC_SST'],
|
55
|
+
execution_id
|
52
56
|
)
|
53
57
|
|
54
58
|
opts = params['development_client']
|
@@ -66,7 +70,8 @@ module GoodData
|
|
66
70
|
opts['verify_ssl'].to_b,
|
67
71
|
opts['username'] || opts['login'] || opts['email'],
|
68
72
|
opts['password'],
|
69
|
-
opts['sst']
|
73
|
+
opts['sst'],
|
74
|
+
execution_id
|
70
75
|
)
|
71
76
|
else
|
72
77
|
development_client = client
|
@@ -77,6 +82,7 @@ module GoodData
|
|
77
82
|
'development_client' => development_client
|
78
83
|
}
|
79
84
|
|
85
|
+
# collect parent project if deployed as process
|
80
86
|
if params['GDC_PROJECT_ID']
|
81
87
|
new_params['gdc_project'] = GoodData.project = client.projects(params['GDC_PROJECT_ID'])
|
82
88
|
end
|
@@ -86,29 +92,29 @@ module GoodData
|
|
86
92
|
# Try to disconnect client
|
87
93
|
begin
|
88
94
|
client.disconnect
|
89
|
-
rescue
|
90
|
-
GoodData.logger.warn(
|
95
|
+
rescue StandardError => e
|
96
|
+
GoodData.logger.warn("Tried to disconnect client. Was unsuccessful. Proceeding anyway. Error: #{e}")
|
91
97
|
end
|
92
98
|
|
93
99
|
# Try to disconnect development_client
|
94
100
|
begin
|
95
101
|
development_client.disconnect if development_client != client
|
96
|
-
rescue
|
97
|
-
GoodData.logger.warn(
|
102
|
+
rescue StandardError => e
|
103
|
+
GoodData.logger.warn("Tried to disconnect development_client. Was unsuccessful. Proceeding anyway. Error: #{e}")
|
98
104
|
end
|
99
105
|
|
100
106
|
returning_value
|
101
107
|
end
|
102
108
|
|
103
109
|
class << self
|
104
|
-
def connect(server, verify_ssl, username, password, sst_token) # rubocop:disable Metrics/ParameterLists
|
110
|
+
def connect(server, verify_ssl, username, password, sst_token, execution_id) # rubocop:disable Metrics/ParameterLists
|
105
111
|
if username.nil? || password.nil?
|
106
112
|
GoodData.logger.info("Connecting with SST to server #{server}")
|
107
113
|
raise 'SST (SuperSecureToken) not present in params' if sst_token.nil?
|
108
|
-
conn = GoodData.connect(sst_token: sst_token, server: server, verify_ssl: verify_ssl)
|
114
|
+
conn = GoodData.connect(sst_token: sst_token, server: server, verify_ssl: verify_ssl, execution_id: execution_id)
|
109
115
|
else
|
110
116
|
GoodData.logger.info("Connecting as #{username} to server #{server}")
|
111
|
-
conn = GoodData.connect(username, password, server: server, verify_ssl: verify_ssl)
|
117
|
+
conn = GoodData.connect(username, password, server: server, verify_ssl: verify_ssl, execution_id: execution_id)
|
112
118
|
end
|
113
119
|
conn.stats_on
|
114
120
|
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
6
6
|
|
7
7
|
require 'logger'
|
8
|
+
require 'gooddata/core/splunk_logger'
|
8
9
|
|
9
10
|
require 'gooddata/extensions/true'
|
10
11
|
require 'gooddata/extensions/false'
|
@@ -20,6 +21,7 @@ using NilExtensions
|
|
20
21
|
|
21
22
|
require_relative 'base_middleware'
|
22
23
|
require_relative 'mask_logger_decorator'
|
24
|
+
require_relative 'context_logger_decorator'
|
23
25
|
|
24
26
|
module GoodData
|
25
27
|
module Bricks
|
@@ -45,6 +47,24 @@ module GoodData
|
|
45
47
|
params['GDC_LOGGER'] = logger
|
46
48
|
GoodData.logging_http_on if params['HTTP_LOGGING'] && params['HTTP_LOGGING'].to_b
|
47
49
|
|
50
|
+
# Initialize splunk logger
|
51
|
+
if params['SPLUNK_LOGGING'] && params['SPLUNK_LOGGING'].to_b
|
52
|
+
GoodData.logger.info "Statistics collecting is turned ON. All the data is anonymous."
|
53
|
+
splunk_logger = SplunkLogger.new params['SPLUNK_LOG_PATH'] || GoodData::DEFAULT_SPLUNKLOG_OUTPUT
|
54
|
+
splunk_logger.level = params['SPLUNK_LOG_LEVEL'] || GoodData::DEFAULT_SPLUNKLOG_LEVEL
|
55
|
+
splunk_logger = splunk_logger.extend(ContextLoggerDecorator)
|
56
|
+
splunk_logger.context_source = GoodData.gd_logger
|
57
|
+
values_to_mask = params['values_to_mask'] || []
|
58
|
+
values_to_mask.concat MaskLoggerDecorator.extract_values params
|
59
|
+
splunk_logger = MaskLoggerDecorator.new(splunk_logger, values_to_mask) if values_to_mask.any?
|
60
|
+
else
|
61
|
+
splunk_logger = NilLogger.new
|
62
|
+
end
|
63
|
+
GoodData.splunk_logging_on splunk_logger
|
64
|
+
|
65
|
+
# Initialize context: Execution ID
|
66
|
+
GoodData.gd_logger.execution_id = params['GDC_EXECUTION_ID'] || SecureRandom.urlsafe_base64(16)
|
67
|
+
|
48
68
|
returning(@app.call(params)) do |_result|
|
49
69
|
logger.info('Pipeline ending')
|
50
70
|
end
|
@@ -14,6 +14,24 @@ module GoodData
|
|
14
14
|
@values_to_mask = values_to_mask
|
15
15
|
end
|
16
16
|
|
17
|
+
class << self
|
18
|
+
# Extract values to mask from structured data
|
19
|
+
# @param values [String] or [Hash] or [Array] structured data to be extracted
|
20
|
+
# @return [[String]] array of all String in values
|
21
|
+
def extract_values(values)
|
22
|
+
if values.is_a?(String)
|
23
|
+
[values]
|
24
|
+
elsif values.is_a?(Hash) || values.is_a?(Array)
|
25
|
+
(values.is_a?(Hash) ? values.values : values).reduce([]) do |strings, item|
|
26
|
+
strings.concat extract_values(item)
|
27
|
+
strings
|
28
|
+
end
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
17
35
|
# log methods to be decorated
|
18
36
|
%i[debug error fatal info unknown warn].each do |level|
|
19
37
|
define_method level do |message|
|
@@ -21,14 +39,26 @@ module GoodData
|
|
21
39
|
end
|
22
40
|
end
|
23
41
|
|
24
|
-
|
42
|
+
# Decorator pretends being inner logger itselfs.
|
43
|
+
# @return inner logger class
|
44
|
+
def class
|
45
|
+
@logger.class
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(severity, message = nil, progname = nil)
|
49
|
+
mask message
|
50
|
+
mask progname
|
51
|
+
@logger.add(severity, message, progname)
|
52
|
+
end
|
25
53
|
|
26
|
-
# Masks given message
|
27
|
-
# @param message
|
28
|
-
# @return masked_message [String] masked message
|
54
|
+
# Masks given message
|
55
|
+
# @param message [String] or [Hash] or [Array] message to mask
|
56
|
+
# @return masked_message [String] or [Hash] or [Array] masked message
|
29
57
|
def mask(message)
|
30
58
|
unless message.nil?
|
31
|
-
|
59
|
+
string = message.to_s
|
60
|
+
|
61
|
+
@values_to_mask.reduce(string) do |masked_message, value_to_mask|
|
32
62
|
masked_message.gsub(value_to_mask, "******")
|
33
63
|
end
|
34
64
|
end
|
data/lib/gooddata/client.rb
CHANGED
@@ -42,8 +42,6 @@ require_relative 'core/core'
|
|
42
42
|
|
43
43
|
module GoodData
|
44
44
|
class << self
|
45
|
-
RELEASE_INFO_PATH = '/gdc/releaseInfo'
|
46
|
-
|
47
45
|
# Initializes required dynamically loaded classes
|
48
46
|
def init_module
|
49
47
|
# Metadata packages, such as report.rb, require this to be loaded first
|
@@ -57,8 +55,9 @@ module GoodData
|
|
57
55
|
end
|
58
56
|
|
59
57
|
# Returns information about the GoodData API as a Hash (e.g. version, release time etc.)
|
58
|
+
# @deprecated The release info endpoint has been deprecated without a replacement.
|
60
59
|
def release_info
|
61
|
-
|
60
|
+
fail 'The release info endpoint has been deprecated without a replacement.'
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
|
2
|
+
# This source code is licensed under the BSD-style license found in the
|
3
|
+
# LICENSE file in the root directory of this source tree.
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
require 'gooddata/bricks/middleware/context_manager'
|
7
|
+
require 'gooddata/rest/rest_aggregator'
|
8
|
+
|
9
|
+
module GoodData
|
10
|
+
# This class delegates messages to multiple loggers
|
11
|
+
# By usage of [ContextManager] and [Rest::Aggregator] it stores information about context of execution
|
12
|
+
class GdLogger
|
13
|
+
severity_map = {
|
14
|
+
debug: Logger::DEBUG,
|
15
|
+
info: Logger::INFO,
|
16
|
+
warn: Logger::WARN,
|
17
|
+
error: Logger::ERROR,
|
18
|
+
fatal: Logger::FATAL,
|
19
|
+
unknown: Logger::UNKNOWN
|
20
|
+
}
|
21
|
+
|
22
|
+
attr_accessor :loggers
|
23
|
+
|
24
|
+
include ContextManager
|
25
|
+
include GoodData::Rest::Aggregator
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@loggers = {}
|
29
|
+
initialize_context
|
30
|
+
initialize_store
|
31
|
+
end
|
32
|
+
|
33
|
+
def logging_on(logger_id, logger)
|
34
|
+
loggers[logger_id] = logger
|
35
|
+
end
|
36
|
+
|
37
|
+
def logging_off(logger_id)
|
38
|
+
loggers[logger_id] = NilLogger.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def logging_on?(logger_id)
|
42
|
+
loggers.key?(logger_id) && !loggers[logger_id].is_a?(NilLogger)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Implementation of common logger methods
|
46
|
+
%i[debug error fatal info unknown warn].each do |severity|
|
47
|
+
# GdLogger delegates all records to all loggers
|
48
|
+
define_method severity do |progname = nil, &block|
|
49
|
+
add(severity_map[severity], nil, progname, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns [True] if any logger satisfies severity level
|
53
|
+
define_method severity.to_s + "?" do
|
54
|
+
test_severity severity
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_severity(severity)
|
59
|
+
(loggers.values.map do |logger|
|
60
|
+
logger.send severity.to_s + "?"
|
61
|
+
end).any?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pass message to multiple loggers. By parameters some loggers might be filtered out and the message might be modified.
|
65
|
+
#
|
66
|
+
# @param severity, severity of record
|
67
|
+
# @param message, message to be logged
|
68
|
+
# @param progname, progname to be logged
|
69
|
+
def add(severity, message, progname, &block)
|
70
|
+
loggers.each do |_, logger|
|
71
|
+
logger.add(severity, message, progname, &block)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Set logger level for specified logger
|
76
|
+
#
|
77
|
+
# @param level, severity level
|
78
|
+
# @param [Symbol] logger_name, logger which severity level should be changed
|
79
|
+
def level(level, logger_name)
|
80
|
+
loggers[logger_name].level = level
|
81
|
+
end
|
82
|
+
|
83
|
+
# Set logger level for all loggers
|
84
|
+
#
|
85
|
+
# @param level, severity level
|
86
|
+
def level=(level)
|
87
|
+
loggers.values.each do |logger|
|
88
|
+
logger.level = level
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -7,6 +7,8 @@
|
|
7
7
|
require 'rest-client'
|
8
8
|
|
9
9
|
require_relative 'nil_logger'
|
10
|
+
require_relative 'splunk_logger'
|
11
|
+
require_relative 'gd_logger'
|
10
12
|
|
11
13
|
module GoodData
|
12
14
|
DEFAULT_LOG_LEVEL = Logger::INFO
|
@@ -17,8 +19,12 @@ module GoodData
|
|
17
19
|
DEFAULT_RESTLOG_OUTPUT = STDOUT
|
18
20
|
DEFAULT_RESTLOGGER_CLASS = Logger
|
19
21
|
|
22
|
+
DEFAULT_SPLUNKLOG_LEVEL = Logger::INFO
|
23
|
+
DEFAULT_SPLUNKLOG_OUTPUT = STDERR
|
24
|
+
DEFAULT_SPLUNKLOGGER_CLASS = SplunkLogger
|
25
|
+
|
20
26
|
class << self
|
21
|
-
attr_accessor :logger, :rest_logger
|
27
|
+
attr_accessor :logger, :rest_logger, :gd_logger
|
22
28
|
attr_writer :stats
|
23
29
|
|
24
30
|
# Turn logging on
|
@@ -80,16 +86,23 @@ module GoodData
|
|
80
86
|
!@rest_logger.instance_of?(NilLogger)
|
81
87
|
end
|
82
88
|
|
83
|
-
|
84
|
-
|
89
|
+
# Turn splunk logging on
|
90
|
+
def splunk_logging_on(logger)
|
91
|
+
gd_logger.logging_on :splunk, logger
|
85
92
|
end
|
86
93
|
|
87
|
-
|
88
|
-
|
94
|
+
# Turn splunk logging off
|
95
|
+
#
|
96
|
+
# ### Example
|
97
|
+
#
|
98
|
+
# GoodData.logging_splunk_off
|
99
|
+
#
|
100
|
+
def splunk_logging_off
|
101
|
+
gd_logger.logging_off :splunk
|
89
102
|
end
|
90
103
|
|
91
|
-
def
|
92
|
-
|
104
|
+
def splunk_logging_on?
|
105
|
+
gd_logger.logging_on? :splunk
|
93
106
|
end
|
94
107
|
|
95
108
|
# Initial setup of logger
|
@@ -101,5 +114,9 @@ module GoodData
|
|
101
114
|
DEFAULT_RESTLOG_OUTPUT,
|
102
115
|
NilLogger
|
103
116
|
)
|
117
|
+
|
118
|
+
# Initial setup of splunk logger
|
119
|
+
GoodData.gd_logger = GdLogger.new
|
120
|
+
GoodData.splunk_logging_off
|
104
121
|
end
|
105
122
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
1
|
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
|
4
2
|
# This source code is licensed under the BSD-style license found in the
|
5
3
|
# LICENSE file in the root directory of this source tree.
|
@@ -19,6 +17,7 @@ module GoodData
|
|
19
17
|
alias_method :info, :debug
|
20
18
|
alias_method :warn, :debug
|
21
19
|
alias_method :error, :debug
|
20
|
+
alias_method :add, :debug
|
22
21
|
|
23
22
|
def debug?
|
24
23
|
false
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
|
2
|
+
# This source code is licensed under the BSD-style license found in the
|
3
|
+
# LICENSE file in the root directory of this source tree.
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module GoodData
|
8
|
+
# Logger that process given message to format readable by splunk
|
9
|
+
class SplunkLogger < Logger
|
10
|
+
def hash_to_string(hash)
|
11
|
+
hash.map { |pair| " #{pair[0]}=#{pair[1]}" }.join ""
|
12
|
+
end
|
13
|
+
|
14
|
+
# If the given message or progname is an instance of Hash, it's reformatted to splunk readable format.
|
15
|
+
# In case that the message or the progname contain new line character log won't be printed out.
|
16
|
+
# Otherwise splunk worker wouldn't process it correctly.
|
17
|
+
def add(severity, message = nil, progname = nil)
|
18
|
+
message = hash_to_string(message) if message.is_a? Hash
|
19
|
+
progname = hash_to_string(progname) if progname.is_a? Hash
|
20
|
+
super(severity, message, progname) unless (progname && progname.include?("\n")) || (message && message.include?("\n"))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|