sdk-reforge 1.11.2 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de9767c4803726a6881d27e59b026d4a1e23d5592effae2f72908f44aed56e23
4
- data.tar.gz: 16ab3c79d14b20136bb15cafd76b641f3dcced86c160a16681cf7666a564ec41
3
+ metadata.gz: 8bd7701858dfa6e149ecf77c03e6912f57a74986e013b2b9557da7c90e39f9b2
4
+ data.tar.gz: 7962dbe8f2e4e6e5d3042b46e0881365d707803ae7ff1e1a328dd412f2872841
5
5
  SHA512:
6
- metadata.gz: c5e8a05f5f01014d9605c68648de26784d9190fdb3ab61d2c3ea55687a35feea851c5f50fbe86a3d21dfd8a0795df4644a656db04098988f8f3e7e030b117ae8
7
- data.tar.gz: fe44f02ea0fffa0982732a4c581b50440ce174e2cd3798208820ca96b4ee2cfebe90e866d8566588c2d795b39bd505234bbd56340a2155869f740cdc60b8b15f
6
+ metadata.gz: eff80aba938418cc6108053256ccaee55fc4f7d5a4c5c8d56178e3dd818ecbfb88e7f1144df65e9d28f6b8de08b640ef83997e11f05f091ac0bedaf01277bacd
7
+ data.tar.gz: dbc13318df9207918cf6bc7343bcac5221532a65dd6ee58fbfd61e014a4ba98fcd1f6f6efa2050cb7bcc0823573cdb24bbd541d9d45df8c5e614bf3aa8ba3fc5
data/.DS_Store ADDED
Binary file
@@ -23,7 +23,7 @@ jobs:
23
23
  strategy:
24
24
  fail-fast: false
25
25
  matrix:
26
- ruby-version: ['3.1','3.2','3.3','3.4']
26
+ ruby-version: ['3.2','3.3','3.4']
27
27
 
28
28
  steps:
29
29
  - uses: actions/checkout@v4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.12.0 - 2025-10-31
4
+
5
+ - Restore log level functionality with LOG_LEVEL_V2 support
6
+ - Make SemanticLogger optional - SDK now works with or without it
7
+ - Add stdlib Logger support as alternative to SemanticLogger
8
+ - Add InternalLogger that automatically uses SemanticLogger or stdlib Logger
9
+ - Add `logger_key` initialization option for configuring dynamic log levels
10
+ - Add `stdlib_formatter` method for stdlib Logger integration
11
+
3
12
  ## 1.11.2 - 2025-10-07
4
13
 
5
14
  - Address OpenSSL issue with vulnerability to truncation attack
data/Gemfile CHANGED
@@ -9,8 +9,6 @@ gem 'uuid'
9
9
 
10
10
  gem 'activesupport', '>= 4'
11
11
 
12
- gem 'semantic_logger', '!= 4.16.0', require: "semantic_logger/sync"
13
-
14
12
  group :development do
15
13
  gem 'allocation_stats'
16
14
  gem 'benchmark-ips'
@@ -21,6 +19,7 @@ group :development do
21
19
  end
22
20
 
23
21
  group :test do
22
+ gem 'semantic_logger', '!= 4.16.0', require: "semantic_logger/sync"
24
23
  gem 'minitest'
25
24
  gem 'minitest-focus'
26
25
  gem 'minitest-reporters'
data/Gemfile.lock CHANGED
@@ -102,7 +102,7 @@ GEM
102
102
  rake (~> 13.0)
103
103
  macaddr (1.7.2)
104
104
  systemu (~> 2.6.5)
105
- mini_portile2 (2.8.7)
105
+ mini_portile2 (2.8.9)
106
106
  minitest (5.22.3)
107
107
  minitest-focus (1.4.0)
108
108
  minitest (>= 4, < 6)
@@ -115,7 +115,7 @@ GEM
115
115
  multi_xml (0.6.0)
116
116
  multipart-post (2.4.0)
117
117
  mutex_m (0.2.0)
118
- nokogiri (1.16.7)
118
+ nokogiri (1.18.9)
119
119
  mini_portile2 (~> 2.8.2)
120
120
  racc (~> 1.4)
121
121
  oauth2 (1.4.11)
@@ -128,7 +128,7 @@ GEM
128
128
  stringio
129
129
  public_suffix (5.0.4)
130
130
  racc (1.8.1)
131
- rack (3.0.16)
131
+ rack (3.1.18)
132
132
  rake (13.1.0)
133
133
  rchardet (1.8.0)
134
134
  rdoc (6.6.3.1)
data/README.md CHANGED
@@ -64,7 +64,107 @@ after_fork do |server, worker|
64
64
  end
65
65
  ```
66
66
 
67
+ ## Dynamic Log Levels
67
68
 
69
+ Reforge supports dynamic log level management for Ruby logging frameworks. This allows you to change log levels in real-time without redeploying your application.
70
+
71
+ Supported loggers:
72
+ - SemanticLogger (optional dependency)
73
+ - Ruby stdlib Logger
74
+
75
+ ### Setup with SemanticLogger
76
+
77
+ Add semantic_logger to your Gemfile:
78
+
79
+ ```ruby
80
+ # Gemfile
81
+ gem "semantic_logger"
82
+ ```
83
+
84
+ ### Plain Ruby
85
+
86
+ ```ruby
87
+ require "semantic_logger"
88
+ require "sdk-reforge"
89
+
90
+ client = Reforge::Client.new(
91
+ sdk_key: ENV['REFORGE_BACKEND_SDK_KEY'],
92
+ logger_key: 'log-levels.default' # optional, this is the default
93
+ )
94
+
95
+ SemanticLogger.sync!
96
+ SemanticLogger.default_level = :trace # Reforge will handle filtering
97
+ SemanticLogger.add_appender(
98
+ io: $stdout,
99
+ formatter: :json,
100
+ filter: client.log_level_client.method(:semantic_filter)
101
+ )
102
+ ```
103
+
104
+ ### With Rails
105
+
106
+ ```ruby
107
+ # Gemfile
108
+ gem "amazing_print"
109
+ gem "rails_semantic_logger"
110
+ ```
111
+
112
+ ```ruby
113
+ # config/application.rb
114
+ $reforge_client = Reforge::Client.new # reads REFORGE_BACKEND_SDK_KEY env var
115
+
116
+ # config/initializers/logging.rb
117
+ SemanticLogger.sync!
118
+ SemanticLogger.default_level = :trace # Reforge will handle filtering
119
+ SemanticLogger.add_appender(
120
+ io: $stdout,
121
+ formatter: Rails.env.development? ? :color : :json,
122
+ filter: $reforge_client.log_level_client.method(:semantic_filter)
123
+ )
124
+ ```
125
+
126
+ ```ruby
127
+ # puma.rb
128
+ on_worker_boot do
129
+ SemanticLogger.reopen
130
+ Reforge.fork
131
+ end
132
+ ```
133
+
134
+ ### With Ruby stdlib Logger
135
+
136
+ If you're using Ruby's standard library Logger, you can use a dynamic formatter:
137
+
138
+ ```ruby
139
+ require "logger"
140
+ require "sdk-reforge"
141
+
142
+ client = Reforge::Client.new(
143
+ sdk_key: ENV['REFORGE_BACKEND_SDK_KEY'],
144
+ logger_key: 'log-levels.default' # optional, this is the default
145
+ )
146
+
147
+ logger = Logger.new($stdout)
148
+ logger.level = Logger::DEBUG # Set to most verbose level, Reforge will handle filtering
149
+ logger.formatter = client.log_level_client.stdlib_formatter('MyApp')
150
+ ```
151
+
152
+ The formatter will check dynamic log levels from Reforge and only output logs that meet the configured threshold.
153
+
154
+ ### Configuration
155
+
156
+ In Reforge Launch, create a `LOG_LEVEL_V2` config with your desired key (default: `log-levels.default`). The config will be evaluated with the following context:
157
+
158
+ ```ruby
159
+ {
160
+ "reforge-sdk-logging" => {
161
+ "lang" => "ruby",
162
+ "logger-path" => "your_app.your_class" # class name converted to lowercase with dots
163
+ }
164
+ }
165
+ ```
166
+
167
+ You can set different log levels for different classes/modules using criteria on the `reforge-sdk-logging.logger-path` property.
68
168
 
69
169
  ## Contributing to reforge sdk for ruby
70
170
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.11.2
1
+ 1.12.0
data/lib/prefab_pb.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  require 'google/protobuf'
6
6
 
7
7
 
8
- descriptor_data = "\n\x0cprefab.proto\x12\x06prefab\"W\n\x14\x43onfigServicePointer\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x13\n\x0bstart_at_id\x18\x02 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x03 \x01(\x03\"\xf7\x04\n\x0b\x43onfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x0f\n\x05\x62ytes\x18\x03 \x01(\x0cH\x00\x12\x10\n\x06\x64ouble\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x05 \x01(\x08H\x00\x12\x31\n\x0fweighted_values\x18\x06 \x01(\x0b\x32\x16.prefab.WeightedValuesH\x00\x12\x33\n\x10limit_definition\x18\x07 \x01(\x0b\x32\x17.prefab.LimitDefinitionH\x00\x12%\n\tlog_level\x18\t \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\n \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x0b \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x12$\n\x08provided\x18\x0c \x01(\x0b\x32\x10.prefab.ProvidedH\x00\x12\'\n\x08\x64uration\x18\x0f \x01(\x0b\x32\x13.prefab.IsoDurationH\x00\x12\x1c\n\x04json\x18\x10 \x01(\x0b\x32\x0c.prefab.JsonH\x00\x12 \n\x06schema\x18\x11 \x01(\x0b\x32\x0e.prefab.SchemaH\x00\x12\x19\n\x0c\x63onfidential\x18\r \x01(\x08H\x01\x88\x01\x01\x12\x19\n\x0c\x64\x65\x63rypt_with\x18\x0e \x01(\tH\x02\x88\x01\x01\x12\x11\n\x04name\x18\x12 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x13 \x01(\tH\x04\x88\x01\x01\x42\x06\n\x04typeB\x0f\n\r_confidentialB\x0f\n\r_decrypt_withB\x07\n\x05_nameB\x0e\n\x0c_description\"\x14\n\x04Json\x12\x0c\n\x04json\x18\x01 \x01(\t\"!\n\x0bIsoDuration\x12\x12\n\ndefinition\x18\x01 \x01(\t\"b\n\x08Provided\x12+\n\x06source\x18\x01 \x01(\x0e\x32\x16.prefab.ProvidedSourceH\x00\x88\x01\x01\x12\x13\n\x06lookup\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_sourceB\t\n\x07_lookup\"B\n\x08IntRange\x12\x12\n\x05start\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x03H\x01\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_end\"\x1c\n\nStringList\x12\x0e\n\x06values\x18\x01 \x03(\t\"C\n\rWeightedValue\x12\x0e\n\x06weight\x18\x01 \x01(\x05\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"~\n\x0eWeightedValues\x12.\n\x0fweighted_values\x18\x01 \x03(\x0b\x32\x15.prefab.WeightedValue\x12\"\n\x15hash_by_property_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x18\n\x16_hash_by_property_name\"X\n\x0e\x41piKeyMetadata\x12\x13\n\x06key_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07user_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_key_idB\n\n\x08_user_idJ\x04\x08\x02\x10\x03\"\xa0\x02\n\x07\x43onfigs\x12\x1f\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x0e.prefab.Config\x12<\n\x16\x63onfig_service_pointer\x18\x02 \x01(\x0b\x32\x1c.prefab.ConfigServicePointer\x12\x34\n\x0f\x61pikey_metadata\x18\x03 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\x12\x17\n\nkeep_alive\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_contextB\r\n\x0b_keep_alive\"\xa4\x04\n\x06\x43onfig\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x12\n\nproject_id\x18\x02 \x01(\x03\x12\x0b\n\x03key\x18\x03 \x01(\t\x12%\n\nchanged_by\x18\x04 \x01(\x0b\x32\x11.prefab.ChangedBy\x12\x1f\n\x04rows\x18\x05 \x03(\x0b\x32\x11.prefab.ConfigRow\x12-\n\x10\x61llowable_values\x18\x06 \x03(\x0b\x32\x13.prefab.ConfigValue\x12\'\n\x0b\x63onfig_type\x18\x07 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x15\n\x08\x64raft_id\x18\x08 \x01(\x03H\x00\x88\x01\x01\x12,\n\nvalue_type\x18\t \x01(\x0e\x32\x18.prefab.Config.ValueType\x12\x1a\n\x12send_to_client_sdk\x18\n \x01(\x08\x12\x17\n\nschema_key\x18\x0b \x01(\tH\x01\x88\x01\x01\"\xb6\x01\n\tValueType\x12\x16\n\x12NOT_SET_VALUE_TYPE\x10\x00\x12\x07\n\x03INT\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05\x42YTES\x10\x03\x12\n\n\x06\x44OUBLE\x10\x04\x12\x08\n\x04\x42OOL\x10\x05\x12\x14\n\x10LIMIT_DEFINITION\x10\x07\x12\r\n\tLOG_LEVEL\x10\t\x12\x0f\n\x0bSTRING_LIST\x10\n\x12\r\n\tINT_RANGE\x10\x0b\x12\x0c\n\x08\x44URATION\x10\x0c\x12\x08\n\x04JSON\x10\rB\x0b\n\t_draft_idB\r\n\x0b_schema_key\"V\n\tChangedBy\x12\x0f\n\x07user_id\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x12\n\napi_key_id\x18\x03 \x01(\t\x12\x15\n\ruser_identity\x18\x04 \x01(\t\"\xe4\x01\n\tConfigRow\x12\x1b\n\x0eproject_env_id\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12(\n\x06values\x18\x02 \x03(\x0b\x32\x18.prefab.ConditionalValue\x12\x35\n\nproperties\x18\x03 \x03(\x0b\x32!.prefab.ConfigRow.PropertiesEntry\x1a\x46\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x11\n\x0f_project_env_id\"[\n\x10\x43onditionalValue\x12#\n\x08\x63riteria\x18\x01 \x03(\x0b\x32\x11.prefab.Criterion\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x96\x06\n\tCriterion\x12\x15\n\rproperty_name\x18\x01 \x01(\t\x12\x35\n\x08operator\x18\x02 \x01(\x0e\x32#.prefab.Criterion.CriterionOperator\x12+\n\x0evalue_to_match\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x8d\x05\n\x11\x43riterionOperator\x12\x0b\n\x07NOT_SET\x10\x00\x12\x11\n\rLOOKUP_KEY_IN\x10\x01\x12\x15\n\x11LOOKUP_KEY_NOT_IN\x10\x02\x12\n\n\x06IN_SEG\x10\x03\x12\x0e\n\nNOT_IN_SEG\x10\x04\x12\x0f\n\x0b\x41LWAYS_TRUE\x10\x05\x12\x12\n\x0ePROP_IS_ONE_OF\x10\x06\x12\x16\n\x12PROP_IS_NOT_ONE_OF\x10\x07\x12\x19\n\x15PROP_ENDS_WITH_ONE_OF\x10\x08\x12!\n\x1dPROP_DOES_NOT_END_WITH_ONE_OF\x10\t\x12\x16\n\x12HIERARCHICAL_MATCH\x10\n\x12\x10\n\x0cIN_INT_RANGE\x10\x0b\x12\x1b\n\x17PROP_STARTS_WITH_ONE_OF\x10\x0c\x12#\n\x1fPROP_DOES_NOT_START_WITH_ONE_OF\x10\r\x12\x18\n\x14PROP_CONTAINS_ONE_OF\x10\x0e\x12 \n\x1cPROP_DOES_NOT_CONTAIN_ONE_OF\x10\x0f\x12\x12\n\x0ePROP_LESS_THAN\x10\x10\x12\x1b\n\x17PROP_LESS_THAN_OR_EQUAL\x10\x11\x12\x15\n\x11PROP_GREATER_THAN\x10\x12\x12\x1e\n\x1aPROP_GREATER_THAN_OR_EQUAL\x10\x13\x12\x0f\n\x0bPROP_BEFORE\x10\x14\x12\x0e\n\nPROP_AFTER\x10\x15\x12\x10\n\x0cPROP_MATCHES\x10\x16\x12\x17\n\x13PROP_DOES_NOT_MATCH\x10\x17\x12\x19\n\x15PROP_SEMVER_LESS_THAN\x10\x18\x12\x15\n\x11PROP_SEMVER_EQUAL\x10\x19\x12\x1c\n\x18PROP_SEMVER_GREATER_THAN\x10\x1a\"\x89\x01\n\x07Loggers\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\x12\x15\n\rinstance_hash\x18\x04 \x01(\t\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\xd9\x01\n\x06Logger\x12\x13\n\x0blogger_name\x18\x01 \x01(\t\x12\x13\n\x06traces\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06\x64\x65\x62ugs\x18\x03 \x01(\x03H\x01\x88\x01\x01\x12\x12\n\x05infos\x18\x04 \x01(\x03H\x02\x88\x01\x01\x12\x12\n\x05warns\x18\x05 \x01(\x03H\x03\x88\x01\x01\x12\x13\n\x06\x65rrors\x18\x06 \x01(\x03H\x04\x88\x01\x01\x12\x13\n\x06\x66\x61tals\x18\x07 \x01(\x03H\x05\x88\x01\x01\x42\t\n\x07_tracesB\t\n\x07_debugsB\x08\n\x06_infosB\x08\n\x06_warnsB\t\n\x07_errorsB\t\n\x07_fatals\"\x16\n\x14LoggerReportResponse\"\xdb\x03\n\rLimitResponse\x12\x0e\n\x06passed\x18\x01 \x01(\x08\x12\x12\n\nexpires_at\x18\x02 \x01(\x03\x12\x16\n\x0e\x65nforced_group\x18\x03 \x01(\t\x12\x16\n\x0e\x63urrent_bucket\x18\x04 \x01(\x03\x12\x14\n\x0cpolicy_group\x18\x05 \x01(\t\x12;\n\x0bpolicy_name\x18\x06 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\x14\n\x0cpolicy_limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x61mount\x18\x08 \x01(\x03\x12\x16\n\x0elimit_reset_at\x18\t \x01(\x03\x12\x39\n\x0csafety_level\x18\n \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"\xa9\x01\n\x10LimitPolicyNames\x12\x0b\n\x07NOT_SET\x10\x00\x12\x14\n\x10SECONDLY_ROLLING\x10\x01\x12\x14\n\x10MINUTELY_ROLLING\x10\x03\x12\x12\n\x0eHOURLY_ROLLING\x10\x05\x12\x11\n\rDAILY_ROLLING\x10\x07\x12\x13\n\x0fMONTHLY_ROLLING\x10\x08\x12\x0c\n\x08INFINITE\x10\t\x12\x12\n\x0eYEARLY_ROLLING\x10\n\"\x99\x02\n\x0cLimitRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x16\n\x0e\x61\x63quire_amount\x18\x02 \x01(\x05\x12\x0e\n\x06groups\x18\x03 \x03(\t\x12:\n\x0elimit_combiner\x18\x04 \x01(\x0e\x32\".prefab.LimitRequest.LimitCombiner\x12\x1e\n\x16\x61llow_partial_response\x18\x05 \x01(\x08\x12\x39\n\x0csafety_level\x18\x06 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"6\n\rLimitCombiner\x12\x0b\n\x07NOT_SET\x10\x00\x12\x0b\n\x07MINIMUM\x10\x01\x12\x0b\n\x07MAXIMUM\x10\x02\"/\n\nContextSet\x12!\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x0f.prefab.Context\"\x96\x01\n\x07\x43ontext\x12\x11\n\x04type\x18\x01 \x01(\tH\x00\x88\x01\x01\x12+\n\x06values\x18\x02 \x03(\x0b\x32\x1b.prefab.Context.ValuesEntry\x1a\x42\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x07\n\x05_type\"\x93\x01\n\x08Identity\x12\x13\n\x06lookup\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x34\n\nattributes\x18\x02 \x03(\x0b\x32 .prefab.Identity.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_lookup\"\xd6\x02\n\x18\x43onfigEvaluationMetaData\x12\x1d\n\x10\x63onfig_row_index\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x03 \x01(\x03H\x02\x88\x01\x01\x12%\n\x04type\x18\x04 \x01(\x0e\x32\x12.prefab.ConfigTypeH\x03\x88\x01\x01\x12\x0f\n\x02id\x18\x05 \x01(\x03H\x04\x88\x01\x01\x12\x31\n\nvalue_type\x18\x06 \x01(\x0e\x32\x18.prefab.Config.ValueTypeH\x05\x88\x01\x01\x42\x13\n\x11_config_row_indexB\x1a\n\x18_conditional_value_indexB\x17\n\x15_weighted_value_indexB\x07\n\x05_typeB\x05\n\x03_idB\r\n\x0b_value_type\"\x8b\x03\n\x11\x43lientConfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x10\n\x06\x64ouble\x18\x03 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x04 \x01(\x08H\x00\x12%\n\tlog_level\x18\x05 \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\x07 \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x08 \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x12*\n\x08\x64uration\x18\t \x01(\x0b\x32\x16.prefab.ClientDurationH\x00\x12\x1c\n\x04json\x18\n \x01(\x0b\x32\x0c.prefab.JsonH\x00\x12I\n\x1a\x63onfig_evaluation_metadata\x18\x06 \x01(\x0b\x32 .prefab.ConfigEvaluationMetaDataH\x01\x88\x01\x01\x42\x06\n\x04typeB\x1d\n\x1b_config_evaluation_metadata\"D\n\x0e\x43lientDuration\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x12\x12\n\ndefinition\x18\x03 \x01(\t\"\xa4\x02\n\x11\x43onfigEvaluations\x12\x35\n\x06values\x18\x01 \x03(\x0b\x32%.prefab.ConfigEvaluations.ValuesEntry\x12\x34\n\x0f\x61pikey_metadata\x18\x02 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x03 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\x1aH\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.prefab.ClientConfigValue:\x02\x38\x01\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_context\"\xa8\x02\n\x0fLimitDefinition\x12;\n\x0bpolicy_name\x18\x02 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\r\n\x05\x62urst\x18\x04 \x01(\x05\x12\x12\n\naccount_id\x18\x05 \x01(\x03\x12\x15\n\rlast_modified\x18\x06 \x01(\x03\x12\x12\n\nreturnable\x18\x07 \x01(\x08\x12\x39\n\x0csafety_level\x18\x08 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"@\n\x0bSafetyLevel\x12\x0b\n\x07NOT_SET\x10\x00\x12\x12\n\x0eL4_BEST_EFFORT\x10\x04\x12\x10\n\x0cL5_BOMBPROOF\x10\x05\"@\n\x10LimitDefinitions\x12,\n\x0b\x64\x65\x66initions\x18\x01 \x03(\x0b\x32\x17.prefab.LimitDefinition\"\x8a\x01\n\x0f\x42ufferedRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x14\n\x0c\x63ontent_type\x18\x06 \x01(\t\x12\x0c\n\x04\x66ifo\x18\x07 \x01(\x08\"\x94\x01\n\x0c\x42\x61tchRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x16\n\x0e\x62\x61tch_template\x18\x06 \x01(\t\x12\x17\n\x0f\x62\x61tch_separator\x18\x07 \x01(\t\" \n\rBasicResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"3\n\x10\x43reationResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0e\n\x06new_id\x18\x02 \x01(\x03\"h\n\x07IdBlock\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\r\n\x05start\x18\x04 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x05 \x01(\x03\"a\n\x0eIdBlockRequest\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\x0c\n\x04size\x18\x04 \x01(\x03\"\x8a\x01\n\x0c\x43ontextShape\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x39\n\x0b\x66ield_types\x18\x02 \x03(\x0b\x32$.prefab.ContextShape.FieldTypesEntry\x1a\x31\n\x0f\x46ieldTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"[\n\rContextShapes\x12$\n\x06shapes\x18\x01 \x03(\x0b\x32\x14.prefab.ContextShape\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"C\n\rEvaluatedKeys\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\x93\x01\n\x0f\x45valuatedConfig\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0e\x63onfig_version\x18\x02 \x01(\x03\x12#\n\x06result\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\x12#\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSet\x12\x11\n\ttimestamp\x18\x05 \x01(\x03\"<\n\x10\x45valuatedConfigs\x12(\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x17.prefab.EvaluatedConfig\"\xc4\x03\n\x17\x43onfigEvaluationCounter\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x16\n\tconfig_id\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x1b\n\x0eselected_index\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x30\n\x0eselected_value\x18\x04 \x01(\x0b\x32\x13.prefab.ConfigValueH\x02\x88\x01\x01\x12\x1d\n\x10\x63onfig_row_index\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x06 \x01(\rH\x04\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x07 \x01(\rH\x05\x88\x01\x01\x12\x36\n\x06reason\x18\x08 \x01(\x0e\x32&.prefab.ConfigEvaluationCounter.Reason\"\x15\n\x06Reason\x12\x0b\n\x07UNKNOWN\x10\x00\x42\x0c\n\n_config_idB\x11\n\x0f_selected_indexB\x11\n\x0f_selected_valueB\x13\n\x11_config_row_indexB\x1a\n\x18_conditional_value_indexB\x17\n\x15_weighted_value_index\"{\n\x17\x43onfigEvaluationSummary\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x04type\x18\x02 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x31\n\x08\x63ounters\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationCounter\"k\n\x19\x43onfigEvaluationSummaries\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x32\n\tsummaries\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationSummary\"Z\n\x15LoggersTelemetryEvent\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\"\x98\x02\n\x0eTelemetryEvent\x12\x36\n\tsummaries\x18\x02 \x01(\x0b\x32!.prefab.ConfigEvaluationSummariesH\x00\x12\x33\n\x10\x65xample_contexts\x18\x03 \x01(\x0b\x32\x17.prefab.ExampleContextsH\x00\x12+\n\x0c\x63lient_stats\x18\x04 \x01(\x0b\x32\x13.prefab.ClientStatsH\x00\x12\x30\n\x07loggers\x18\x05 \x01(\x0b\x32\x1d.prefab.LoggersTelemetryEventH\x00\x12/\n\x0e\x63ontext_shapes\x18\x06 \x01(\x0b\x32\x15.prefab.ContextShapesH\x00\x42\t\n\x07payload\"P\n\x0fTelemetryEvents\x12\x15\n\rinstance_hash\x18\x01 \x01(\t\x12&\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x16.prefab.TelemetryEvent\"*\n\x17TelemetryEventsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\";\n\x0f\x45xampleContexts\x12(\n\x08\x65xamples\x18\x01 \x03(\x0b\x32\x16.prefab.ExampleContext\"K\n\x0e\x45xampleContext\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12&\n\ncontextSet\x18\x02 \x01(\x0b\x32\x12.prefab.ContextSet\"F\n\x0b\x43lientStats\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x1b\n\x13\x64ropped_event_count\x18\x03 \x01(\x04\"}\n\x06Schema\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12.\n\x0bschema_type\x18\x02 \x01(\x0e\x32\x19.prefab.Schema.SchemaType\"3\n\nSchemaType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03ZOD\x10\x01\x12\x0f\n\x0bJSON_SCHEMA\x10\x02*:\n\x0eProvidedSource\x12\x1b\n\x17PROVIDED_SOURCE_NOT_SET\x10\x00\x12\x0b\n\x07\x45NV_VAR\x10\x01*\x8e\x01\n\nConfigType\x12\x17\n\x13NOT_SET_CONFIG_TYPE\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\x10\n\x0c\x46\x45\x41TURE_FLAG\x10\x02\x12\r\n\tLOG_LEVEL\x10\x03\x12\x0b\n\x07SEGMENT\x10\x04\x12\x14\n\x10LIMIT_DEFINITION\x10\x05\x12\x0b\n\x07\x44\x45LETED\x10\x06\x12\n\n\x06SCHEMA\x10\x07*a\n\x08LogLevel\x12\x15\n\x11NOT_SET_LOG_LEVEL\x10\x00\x12\t\n\x05TRACE\x10\x01\x12\t\n\x05\x44\x45\x42UG\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\x08\n\x04WARN\x10\x05\x12\t\n\x05\x45RROR\x10\x06\x12\t\n\x05\x46\x41TAL\x10\t*G\n\tOnFailure\x12\x0b\n\x07NOT_SET\x10\x00\x12\x10\n\x0cLOG_AND_PASS\x10\x01\x12\x10\n\x0cLOG_AND_FAIL\x10\x02\x12\t\n\x05THROW\x10\x03\x42L\n\x13\x63loud.prefab.domainB\x06PrefabZ-github.com/prefab-cloud/prefab-cloud-go/protob\x06proto3"
8
+ descriptor_data = "\n\x0cprefab.proto\x12\x06prefab\"W\n\x14\x43onfigServicePointer\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x13\n\x0bstart_at_id\x18\x02 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x03 \x01(\x03\"\xf7\x04\n\x0b\x43onfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x0f\n\x05\x62ytes\x18\x03 \x01(\x0cH\x00\x12\x10\n\x06\x64ouble\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x05 \x01(\x08H\x00\x12\x31\n\x0fweighted_values\x18\x06 \x01(\x0b\x32\x16.prefab.WeightedValuesH\x00\x12\x33\n\x10limit_definition\x18\x07 \x01(\x0b\x32\x17.prefab.LimitDefinitionH\x00\x12%\n\tlog_level\x18\t \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\n \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x0b \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x12$\n\x08provided\x18\x0c \x01(\x0b\x32\x10.prefab.ProvidedH\x00\x12\'\n\x08\x64uration\x18\x0f \x01(\x0b\x32\x13.prefab.IsoDurationH\x00\x12\x1c\n\x04json\x18\x10 \x01(\x0b\x32\x0c.prefab.JsonH\x00\x12 \n\x06schema\x18\x11 \x01(\x0b\x32\x0e.prefab.SchemaH\x00\x12\x19\n\x0c\x63onfidential\x18\r \x01(\x08H\x01\x88\x01\x01\x12\x19\n\x0c\x64\x65\x63rypt_with\x18\x0e \x01(\tH\x02\x88\x01\x01\x12\x11\n\x04name\x18\x12 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x13 \x01(\tH\x04\x88\x01\x01\x42\x06\n\x04typeB\x0f\n\r_confidentialB\x0f\n\r_decrypt_withB\x07\n\x05_nameB\x0e\n\x0c_description\"\x14\n\x04Json\x12\x0c\n\x04json\x18\x01 \x01(\t\"!\n\x0bIsoDuration\x12\x12\n\ndefinition\x18\x01 \x01(\t\"b\n\x08Provided\x12+\n\x06source\x18\x01 \x01(\x0e\x32\x16.prefab.ProvidedSourceH\x00\x88\x01\x01\x12\x13\n\x06lookup\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_sourceB\t\n\x07_lookup\"B\n\x08IntRange\x12\x12\n\x05start\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x03H\x01\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_end\"\x1c\n\nStringList\x12\x0e\n\x06values\x18\x01 \x03(\t\"C\n\rWeightedValue\x12\x0e\n\x06weight\x18\x01 \x01(\x05\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"~\n\x0eWeightedValues\x12.\n\x0fweighted_values\x18\x01 \x03(\x0b\x32\x15.prefab.WeightedValue\x12\"\n\x15hash_by_property_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x18\n\x16_hash_by_property_name\"X\n\x0e\x41piKeyMetadata\x12\x13\n\x06key_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07user_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_key_idB\n\n\x08_user_idJ\x04\x08\x02\x10\x03\"\xa0\x02\n\x07\x43onfigs\x12\x1f\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x0e.prefab.Config\x12<\n\x16\x63onfig_service_pointer\x18\x02 \x01(\x0b\x32\x1c.prefab.ConfigServicePointer\x12\x34\n\x0f\x61pikey_metadata\x18\x03 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\x12\x17\n\nkeep_alive\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_contextB\r\n\x0b_keep_alive\"\xa4\x04\n\x06\x43onfig\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x12\n\nproject_id\x18\x02 \x01(\x03\x12\x0b\n\x03key\x18\x03 \x01(\t\x12%\n\nchanged_by\x18\x04 \x01(\x0b\x32\x11.prefab.ChangedBy\x12\x1f\n\x04rows\x18\x05 \x03(\x0b\x32\x11.prefab.ConfigRow\x12-\n\x10\x61llowable_values\x18\x06 \x03(\x0b\x32\x13.prefab.ConfigValue\x12\'\n\x0b\x63onfig_type\x18\x07 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x15\n\x08\x64raft_id\x18\x08 \x01(\x03H\x00\x88\x01\x01\x12,\n\nvalue_type\x18\t \x01(\x0e\x32\x18.prefab.Config.ValueType\x12\x1a\n\x12send_to_client_sdk\x18\n \x01(\x08\x12\x17\n\nschema_key\x18\x0b \x01(\tH\x01\x88\x01\x01\"\xb6\x01\n\tValueType\x12\x16\n\x12NOT_SET_VALUE_TYPE\x10\x00\x12\x07\n\x03INT\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05\x42YTES\x10\x03\x12\n\n\x06\x44OUBLE\x10\x04\x12\x08\n\x04\x42OOL\x10\x05\x12\x14\n\x10LIMIT_DEFINITION\x10\x07\x12\r\n\tLOG_LEVEL\x10\t\x12\x0f\n\x0bSTRING_LIST\x10\n\x12\r\n\tINT_RANGE\x10\x0b\x12\x0c\n\x08\x44URATION\x10\x0c\x12\x08\n\x04JSON\x10\rB\x0b\n\t_draft_idB\r\n\x0b_schema_key\"V\n\tChangedBy\x12\x0f\n\x07user_id\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x12\n\napi_key_id\x18\x03 \x01(\t\x12\x15\n\ruser_identity\x18\x04 \x01(\t\"\xe4\x01\n\tConfigRow\x12\x1b\n\x0eproject_env_id\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12(\n\x06values\x18\x02 \x03(\x0b\x32\x18.prefab.ConditionalValue\x12\x35\n\nproperties\x18\x03 \x03(\x0b\x32!.prefab.ConfigRow.PropertiesEntry\x1a\x46\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x11\n\x0f_project_env_id\"[\n\x10\x43onditionalValue\x12#\n\x08\x63riteria\x18\x01 \x03(\x0b\x32\x11.prefab.Criterion\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x96\x06\n\tCriterion\x12\x15\n\rproperty_name\x18\x01 \x01(\t\x12\x35\n\x08operator\x18\x02 \x01(\x0e\x32#.prefab.Criterion.CriterionOperator\x12+\n\x0evalue_to_match\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x8d\x05\n\x11\x43riterionOperator\x12\x0b\n\x07NOT_SET\x10\x00\x12\x11\n\rLOOKUP_KEY_IN\x10\x01\x12\x15\n\x11LOOKUP_KEY_NOT_IN\x10\x02\x12\n\n\x06IN_SEG\x10\x03\x12\x0e\n\nNOT_IN_SEG\x10\x04\x12\x0f\n\x0b\x41LWAYS_TRUE\x10\x05\x12\x12\n\x0ePROP_IS_ONE_OF\x10\x06\x12\x16\n\x12PROP_IS_NOT_ONE_OF\x10\x07\x12\x19\n\x15PROP_ENDS_WITH_ONE_OF\x10\x08\x12!\n\x1dPROP_DOES_NOT_END_WITH_ONE_OF\x10\t\x12\x16\n\x12HIERARCHICAL_MATCH\x10\n\x12\x10\n\x0cIN_INT_RANGE\x10\x0b\x12\x1b\n\x17PROP_STARTS_WITH_ONE_OF\x10\x0c\x12#\n\x1fPROP_DOES_NOT_START_WITH_ONE_OF\x10\r\x12\x18\n\x14PROP_CONTAINS_ONE_OF\x10\x0e\x12 \n\x1cPROP_DOES_NOT_CONTAIN_ONE_OF\x10\x0f\x12\x12\n\x0ePROP_LESS_THAN\x10\x10\x12\x1b\n\x17PROP_LESS_THAN_OR_EQUAL\x10\x11\x12\x15\n\x11PROP_GREATER_THAN\x10\x12\x12\x1e\n\x1aPROP_GREATER_THAN_OR_EQUAL\x10\x13\x12\x0f\n\x0bPROP_BEFORE\x10\x14\x12\x0e\n\nPROP_AFTER\x10\x15\x12\x10\n\x0cPROP_MATCHES\x10\x16\x12\x17\n\x13PROP_DOES_NOT_MATCH\x10\x17\x12\x19\n\x15PROP_SEMVER_LESS_THAN\x10\x18\x12\x15\n\x11PROP_SEMVER_EQUAL\x10\x19\x12\x1c\n\x18PROP_SEMVER_GREATER_THAN\x10\x1a\"\x89\x01\n\x07Loggers\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\x12\x15\n\rinstance_hash\x18\x04 \x01(\t\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\xd9\x01\n\x06Logger\x12\x13\n\x0blogger_name\x18\x01 \x01(\t\x12\x13\n\x06traces\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06\x64\x65\x62ugs\x18\x03 \x01(\x03H\x01\x88\x01\x01\x12\x12\n\x05infos\x18\x04 \x01(\x03H\x02\x88\x01\x01\x12\x12\n\x05warns\x18\x05 \x01(\x03H\x03\x88\x01\x01\x12\x13\n\x06\x65rrors\x18\x06 \x01(\x03H\x04\x88\x01\x01\x12\x13\n\x06\x66\x61tals\x18\x07 \x01(\x03H\x05\x88\x01\x01\x42\t\n\x07_tracesB\t\n\x07_debugsB\x08\n\x06_infosB\x08\n\x06_warnsB\t\n\x07_errorsB\t\n\x07_fatals\"\x16\n\x14LoggerReportResponse\"\xdb\x03\n\rLimitResponse\x12\x0e\n\x06passed\x18\x01 \x01(\x08\x12\x12\n\nexpires_at\x18\x02 \x01(\x03\x12\x16\n\x0e\x65nforced_group\x18\x03 \x01(\t\x12\x16\n\x0e\x63urrent_bucket\x18\x04 \x01(\x03\x12\x14\n\x0cpolicy_group\x18\x05 \x01(\t\x12;\n\x0bpolicy_name\x18\x06 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\x14\n\x0cpolicy_limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x61mount\x18\x08 \x01(\x03\x12\x16\n\x0elimit_reset_at\x18\t \x01(\x03\x12\x39\n\x0csafety_level\x18\n \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"\xa9\x01\n\x10LimitPolicyNames\x12\x0b\n\x07NOT_SET\x10\x00\x12\x14\n\x10SECONDLY_ROLLING\x10\x01\x12\x14\n\x10MINUTELY_ROLLING\x10\x03\x12\x12\n\x0eHOURLY_ROLLING\x10\x05\x12\x11\n\rDAILY_ROLLING\x10\x07\x12\x13\n\x0fMONTHLY_ROLLING\x10\x08\x12\x0c\n\x08INFINITE\x10\t\x12\x12\n\x0eYEARLY_ROLLING\x10\n\"\x99\x02\n\x0cLimitRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x16\n\x0e\x61\x63quire_amount\x18\x02 \x01(\x05\x12\x0e\n\x06groups\x18\x03 \x03(\t\x12:\n\x0elimit_combiner\x18\x04 \x01(\x0e\x32\".prefab.LimitRequest.LimitCombiner\x12\x1e\n\x16\x61llow_partial_response\x18\x05 \x01(\x08\x12\x39\n\x0csafety_level\x18\x06 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"6\n\rLimitCombiner\x12\x0b\n\x07NOT_SET\x10\x00\x12\x0b\n\x07MINIMUM\x10\x01\x12\x0b\n\x07MAXIMUM\x10\x02\"/\n\nContextSet\x12!\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x0f.prefab.Context\"\x96\x01\n\x07\x43ontext\x12\x11\n\x04type\x18\x01 \x01(\tH\x00\x88\x01\x01\x12+\n\x06values\x18\x02 \x03(\x0b\x32\x1b.prefab.Context.ValuesEntry\x1a\x42\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x07\n\x05_type\"\x93\x01\n\x08Identity\x12\x13\n\x06lookup\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x34\n\nattributes\x18\x02 \x03(\x0b\x32 .prefab.Identity.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_lookup\"\xd6\x02\n\x18\x43onfigEvaluationMetaData\x12\x1d\n\x10\x63onfig_row_index\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x03 \x01(\x03H\x02\x88\x01\x01\x12%\n\x04type\x18\x04 \x01(\x0e\x32\x12.prefab.ConfigTypeH\x03\x88\x01\x01\x12\x0f\n\x02id\x18\x05 \x01(\x03H\x04\x88\x01\x01\x12\x31\n\nvalue_type\x18\x06 \x01(\x0e\x32\x18.prefab.Config.ValueTypeH\x05\x88\x01\x01\x42\x13\n\x11_config_row_indexB\x1a\n\x18_conditional_value_indexB\x17\n\x15_weighted_value_indexB\x07\n\x05_typeB\x05\n\x03_idB\r\n\x0b_value_type\"\x8b\x03\n\x11\x43lientConfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x10\n\x06\x64ouble\x18\x03 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x04 \x01(\x08H\x00\x12%\n\tlog_level\x18\x05 \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\x07 \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x08 \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x12*\n\x08\x64uration\x18\t \x01(\x0b\x32\x16.prefab.ClientDurationH\x00\x12\x1c\n\x04json\x18\n \x01(\x0b\x32\x0c.prefab.JsonH\x00\x12I\n\x1a\x63onfig_evaluation_metadata\x18\x06 \x01(\x0b\x32 .prefab.ConfigEvaluationMetaDataH\x01\x88\x01\x01\x42\x06\n\x04typeB\x1d\n\x1b_config_evaluation_metadata\"D\n\x0e\x43lientDuration\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x12\x12\n\ndefinition\x18\x03 \x01(\t\"\xa4\x02\n\x11\x43onfigEvaluations\x12\x35\n\x06values\x18\x01 \x03(\x0b\x32%.prefab.ConfigEvaluations.ValuesEntry\x12\x34\n\x0f\x61pikey_metadata\x18\x02 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x03 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\x1aH\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.prefab.ClientConfigValue:\x02\x38\x01\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_context\"\xa8\x02\n\x0fLimitDefinition\x12;\n\x0bpolicy_name\x18\x02 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\r\n\x05\x62urst\x18\x04 \x01(\x05\x12\x12\n\naccount_id\x18\x05 \x01(\x03\x12\x15\n\rlast_modified\x18\x06 \x01(\x03\x12\x12\n\nreturnable\x18\x07 \x01(\x08\x12\x39\n\x0csafety_level\x18\x08 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"@\n\x0bSafetyLevel\x12\x0b\n\x07NOT_SET\x10\x00\x12\x12\n\x0eL4_BEST_EFFORT\x10\x04\x12\x10\n\x0cL5_BOMBPROOF\x10\x05\"@\n\x10LimitDefinitions\x12,\n\x0b\x64\x65\x66initions\x18\x01 \x03(\x0b\x32\x17.prefab.LimitDefinition\"\x8a\x01\n\x0f\x42ufferedRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x14\n\x0c\x63ontent_type\x18\x06 \x01(\t\x12\x0c\n\x04\x66ifo\x18\x07 \x01(\x08\"\x94\x01\n\x0c\x42\x61tchRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x16\n\x0e\x62\x61tch_template\x18\x06 \x01(\t\x12\x17\n\x0f\x62\x61tch_separator\x18\x07 \x01(\t\" \n\rBasicResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"3\n\x10\x43reationResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0e\n\x06new_id\x18\x02 \x01(\x03\"h\n\x07IdBlock\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\r\n\x05start\x18\x04 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x05 \x01(\x03\"a\n\x0eIdBlockRequest\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\x0c\n\x04size\x18\x04 \x01(\x03\"\x8a\x01\n\x0c\x43ontextShape\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x39\n\x0b\x66ield_types\x18\x02 \x03(\x0b\x32$.prefab.ContextShape.FieldTypesEntry\x1a\x31\n\x0f\x46ieldTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"[\n\rContextShapes\x12$\n\x06shapes\x18\x01 \x03(\x0b\x32\x14.prefab.ContextShape\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"C\n\rEvaluatedKeys\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\x93\x01\n\x0f\x45valuatedConfig\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0e\x63onfig_version\x18\x02 \x01(\x03\x12#\n\x06result\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\x12#\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSet\x12\x11\n\ttimestamp\x18\x05 \x01(\x03\"<\n\x10\x45valuatedConfigs\x12(\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x17.prefab.EvaluatedConfig\"\xc4\x03\n\x17\x43onfigEvaluationCounter\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x16\n\tconfig_id\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x1b\n\x0eselected_index\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x30\n\x0eselected_value\x18\x04 \x01(\x0b\x32\x13.prefab.ConfigValueH\x02\x88\x01\x01\x12\x1d\n\x10\x63onfig_row_index\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x06 \x01(\rH\x04\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x07 \x01(\rH\x05\x88\x01\x01\x12\x36\n\x06reason\x18\x08 \x01(\x0e\x32&.prefab.ConfigEvaluationCounter.Reason\"\x15\n\x06Reason\x12\x0b\n\x07UNKNOWN\x10\x00\x42\x0c\n\n_config_idB\x11\n\x0f_selected_indexB\x11\n\x0f_selected_valueB\x13\n\x11_config_row_indexB\x1a\n\x18_conditional_value_indexB\x17\n\x15_weighted_value_index\"{\n\x17\x43onfigEvaluationSummary\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x04type\x18\x02 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x31\n\x08\x63ounters\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationCounter\"k\n\x19\x43onfigEvaluationSummaries\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x32\n\tsummaries\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationSummary\"Z\n\x15LoggersTelemetryEvent\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\"\x98\x02\n\x0eTelemetryEvent\x12\x36\n\tsummaries\x18\x02 \x01(\x0b\x32!.prefab.ConfigEvaluationSummariesH\x00\x12\x33\n\x10\x65xample_contexts\x18\x03 \x01(\x0b\x32\x17.prefab.ExampleContextsH\x00\x12+\n\x0c\x63lient_stats\x18\x04 \x01(\x0b\x32\x13.prefab.ClientStatsH\x00\x12\x30\n\x07loggers\x18\x05 \x01(\x0b\x32\x1d.prefab.LoggersTelemetryEventH\x00\x12/\n\x0e\x63ontext_shapes\x18\x06 \x01(\x0b\x32\x15.prefab.ContextShapesH\x00\x42\t\n\x07payload\"P\n\x0fTelemetryEvents\x12\x15\n\rinstance_hash\x18\x01 \x01(\t\x12&\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x16.prefab.TelemetryEvent\"*\n\x17TelemetryEventsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\";\n\x0f\x45xampleContexts\x12(\n\x08\x65xamples\x18\x01 \x03(\x0b\x32\x16.prefab.ExampleContext\"K\n\x0e\x45xampleContext\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12&\n\ncontextSet\x18\x02 \x01(\x0b\x32\x12.prefab.ContextSet\"F\n\x0b\x43lientStats\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x1b\n\x13\x64ropped_event_count\x18\x03 \x01(\x04\"}\n\x06Schema\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12.\n\x0bschema_type\x18\x02 \x01(\x0e\x32\x19.prefab.Schema.SchemaType\"3\n\nSchemaType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03ZOD\x10\x01\x12\x0f\n\x0bJSON_SCHEMA\x10\x02*:\n\x0eProvidedSource\x12\x1b\n\x17PROVIDED_SOURCE_NOT_SET\x10\x00\x12\x0b\n\x07\x45NV_VAR\x10\x01*\xa0\x01\n\nConfigType\x12\x17\n\x13NOT_SET_CONFIG_TYPE\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\x10\n\x0c\x46\x45\x41TURE_FLAG\x10\x02\x12\r\n\tLOG_LEVEL\x10\x03\x12\x0b\n\x07SEGMENT\x10\x04\x12\x14\n\x10LIMIT_DEFINITION\x10\x05\x12\x0b\n\x07\x44\x45LETED\x10\x06\x12\n\n\x06SCHEMA\x10\x07\x12\x10\n\x0cLOG_LEVEL_V2\x10\x08*a\n\x08LogLevel\x12\x15\n\x11NOT_SET_LOG_LEVEL\x10\x00\x12\t\n\x05TRACE\x10\x01\x12\t\n\x05\x44\x45\x42UG\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\x08\n\x04WARN\x10\x05\x12\t\n\x05\x45RROR\x10\x06\x12\t\n\x05\x46\x41TAL\x10\t*G\n\tOnFailure\x12\x0b\n\x07NOT_SET\x10\x00\x12\x10\n\x0cLOG_AND_PASS\x10\x01\x12\x10\n\x0cLOG_AND_FAIL\x10\x02\x12\t\n\x05THROW\x10\x03\x42L\n\x13\x63loud.prefab.domainB\x06PrefabZ-github.com/prefab-cloud/prefab-cloud-go/protob\x06proto3"
9
9
 
10
10
  pool = ::Google::Protobuf::DescriptorPool.generated_pool
11
11
  pool.add_serialized_file(descriptor_data)
@@ -53,6 +53,10 @@ module Reforge
53
53
  @feature_flag_client ||= Reforge::FeatureFlagClient.new(self)
54
54
  end
55
55
 
56
+ def log_level_client
57
+ @log_level_client ||= Reforge::LogLevelClient.new(self)
58
+ end
59
+
56
60
  def context_shape_aggregator
57
61
  return nil if @options.collect_max_shapes <= 0
58
62
 
@@ -1,43 +1,173 @@
1
- module Reforge
2
- class InternalLogger < SemanticLogger::Logger
1
+ # frozen_string_literal: true
3
2
 
3
+ module Reforge
4
+ # Internal logger for the Reforge SDK
5
+ # Uses SemanticLogger if available, falls back to stdlib Logger
6
+ class InternalLogger
4
7
  def initialize(klass)
5
- default_level = ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'] ? ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'].downcase.to_sym : :warn
6
- super(klass, default_level)
8
+ @klass = klass
9
+ @level_sym = nil # Track the symbol level for consistency
10
+
11
+ if defined?(SemanticLogger)
12
+ @logger = create_semantic_logger
13
+ @using_semantic = true
14
+ else
15
+ @logger = create_stdlib_logger
16
+ @using_semantic = false
17
+ end
18
+
19
+ # Track all instances regardless of logger type
7
20
  instances << self
8
21
  end
9
22
 
10
- def log(log, message = nil, progname = nil, &block)
11
- return if recurse_check[local_log_id]
12
- recurse_check[local_log_id] = true
13
- begin
14
- super(log, message, progname, &block)
15
- ensure
16
- recurse_check[local_log_id] = false
23
+ # Log methods
24
+ def trace(message = nil, &block)
25
+ log_message(:trace, message, &block)
26
+ end
27
+
28
+ def debug(message = nil, &block)
29
+ log_message(:debug, message, &block)
30
+ end
31
+
32
+ def info(message = nil, &block)
33
+ log_message(:info, message, &block)
34
+ end
35
+
36
+ def warn(message = nil, &block)
37
+ log_message(:warn, message, &block)
38
+ end
39
+
40
+ def error(message = nil, &block)
41
+ log_message(:error, message, &block)
42
+ end
43
+
44
+ def fatal(message = nil, &block)
45
+ log_message(:fatal, message, &block)
46
+ end
47
+
48
+ def level
49
+ if @using_semantic
50
+ @logger.level
51
+ else
52
+ # Return the symbol level we tracked, or map from Logger constant
53
+ @level_sym || case @logger.level
54
+ when Logger::DEBUG then :debug
55
+ when Logger::INFO then :info
56
+ when Logger::WARN then :warn
57
+ when Logger::ERROR then :error
58
+ when Logger::FATAL then :fatal
59
+ else :warn
60
+ end
17
61
  end
18
62
  end
19
63
 
20
- def local_log_id
21
- Thread.current.__id__
64
+ def level=(new_level)
65
+ if @using_semantic
66
+ @logger.level = new_level
67
+ else
68
+ # Track the symbol level for consistency
69
+ @level_sym = new_level
70
+
71
+ # Map symbol to Logger constant
72
+ @logger.level = case new_level
73
+ when :trace, :debug then Logger::DEBUG
74
+ when :info then Logger::INFO
75
+ when :warn then Logger::WARN
76
+ when :error then Logger::ERROR
77
+ when :fatal then Logger::FATAL
78
+ else Logger::WARN
79
+ end
80
+ end
22
81
  end
23
82
 
24
83
  # Our client outputs debug logging,
25
84
  # but if you aren't using Reforge logging this could be too chatty.
26
85
  # If you aren't using reforge log filter, only log warn level and above
27
86
  def self.using_reforge_log_filter!
28
- @@instances.each do |l|
29
- l.level = :trace
87
+ @@instances&.each do |logger|
88
+ logger.level = :trace
30
89
  end
31
90
  end
32
91
 
33
92
  private
34
93
 
35
- def instances
36
- @@instances ||= []
94
+ def create_semantic_logger
95
+ default_level = env_log_level || :warn
96
+ logger = SemanticLogger::Logger.new(@klass, default_level)
97
+
98
+ # Wrap to prevent recursion
99
+ class << logger
100
+ def log(log, message = nil, progname = nil, &block)
101
+ return if recurse_check[local_log_id]
102
+ recurse_check[local_log_id] = true
103
+ begin
104
+ super(log, message, progname, &block)
105
+ ensure
106
+ recurse_check[local_log_id] = false
107
+ end
108
+ end
109
+
110
+ def local_log_id
111
+ Thread.current.__id__
112
+ end
113
+
114
+ private
115
+
116
+ def recurse_check
117
+ @recurse_check ||= Concurrent::Map.new(initial_capacity: 2)
118
+ end
119
+ end
120
+
121
+ logger
122
+ end
123
+
124
+ def create_stdlib_logger
125
+ require 'logger'
126
+ # When using stdlib Logger (no SemanticLogger), write to $stderr only
127
+ # Tests use $logs for SemanticLogger-filtered output, not stdlib Logger
128
+ logger = Logger.new($stderr)
129
+
130
+ # When SemanticLogger is not available, default to :warn to match SemanticLogger behavior
131
+ default_level_sym = :warn
132
+ @level_sym = env_log_level || default_level_sym
133
+
134
+ logger.level = case @level_sym
135
+ when :trace, :debug then Logger::DEBUG
136
+ when :info then Logger::INFO
137
+ when :warn then Logger::WARN
138
+ when :error then Logger::ERROR
139
+ when :fatal then Logger::FATAL
140
+ else Logger::WARN
141
+ end
142
+ logger.progname = @klass.to_s
143
+
144
+ # Use a custom formatter that mimics SemanticLogger format
145
+ # SemanticLogger format: "ClassName -- Message"
146
+ # This helps tests that expect SemanticLogger-style output
147
+ logger.formatter = proc do |severity, datetime, progname, msg|
148
+ "#{progname} -- #{msg}\n"
149
+ end
150
+
151
+ logger
152
+ end
153
+
154
+ def env_log_level
155
+ level_str = ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL']
156
+ level_str&.downcase&.to_sym
37
157
  end
38
158
 
39
- def recurse_check
40
- @recurse_check ||=Concurrent::Map.new(initial_capacity: 2)
159
+ def log_message(level, message, &block)
160
+ if @using_semantic
161
+ @logger.send(level, message, &block)
162
+ else
163
+ # stdlib Logger doesn't have trace
164
+ level = :debug if level == :trace
165
+ @logger.send(level, message || block&.call)
166
+ end
167
+ end
168
+
169
+ def instances
170
+ @@instances ||= []
41
171
  end
42
172
  end
43
173
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reforge
4
+ # Public LogLevel enum that maps to the underlying Prefab.Config.LogLevel
5
+ module LogLevel
6
+ TRACE = :trace
7
+ DEBUG = :debug
8
+ INFO = :info
9
+ WARN = :warn
10
+ ERROR = :error
11
+ FATAL = :fatal
12
+
13
+ # Map from PrefabProto::LogLevel enum symbols (uppercase) to our public LogLevel constants (lowercase)
14
+ # When unwrapped, the proto returns uppercase symbols like :INFO, :DEBUG, etc.
15
+ PROTO_SYMBOL_TO_LOG_LEVEL = {
16
+ :NOT_SET_LOG_LEVEL => DEBUG,
17
+ :TRACE => TRACE,
18
+ :DEBUG => DEBUG,
19
+ :INFO => INFO,
20
+ :WARN => WARN,
21
+ :ERROR => ERROR,
22
+ :FATAL => FATAL
23
+ }.freeze
24
+
25
+ # Map from PrefabProto::LogLevel enum integer values to our public LogLevel constants
26
+ PROTO_INT_TO_LOG_LEVEL = {
27
+ PrefabProto::LogLevel::NOT_SET_LOG_LEVEL => DEBUG,
28
+ PrefabProto::LogLevel::TRACE => TRACE,
29
+ PrefabProto::LogLevel::DEBUG => DEBUG,
30
+ PrefabProto::LogLevel::INFO => INFO,
31
+ PrefabProto::LogLevel::WARN => WARN,
32
+ PrefabProto::LogLevel::ERROR => ERROR,
33
+ PrefabProto::LogLevel::FATAL => FATAL
34
+ }.freeze
35
+
36
+ def self.from_proto(proto_log_level)
37
+ case proto_log_level
38
+ when Symbol
39
+ PROTO_SYMBOL_TO_LOG_LEVEL.fetch(proto_log_level, DEBUG)
40
+ when Integer
41
+ PROTO_INT_TO_LOG_LEVEL.fetch(proto_log_level, DEBUG)
42
+ else
43
+ DEBUG
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reforge
4
+ class LogLevelClient
5
+ LOG = Reforge::InternalLogger.new(self)
6
+
7
+ # Map from our LogLevel symbols to SemanticLogger numeric severity levels
8
+ # SemanticLogger levels: trace=0, debug=1, info=2, warn=3, error=4, fatal=5
9
+ SEMANTIC_LOGGER_LEVELS = {
10
+ trace: 0,
11
+ debug: 1,
12
+ info: 2,
13
+ warn: 3,
14
+ error: 4,
15
+ fatal: 5
16
+ }.freeze
17
+
18
+ def initialize(base_client)
19
+ @base_client = base_client
20
+ end
21
+
22
+ # Map from Ruby stdlib Logger severity levels to our LogLevel symbols
23
+ # Ruby Logger levels: DEBUG=0, INFO=1, WARN=2, ERROR=3, FATAL=4, UNKNOWN=5
24
+ STDLIB_LOGGER_LEVELS = {
25
+ 0 => :debug, # Logger::DEBUG
26
+ 1 => :info, # Logger::INFO
27
+ 2 => :warn, # Logger::WARN
28
+ 3 => :error, # Logger::ERROR
29
+ 4 => :fatal, # Logger::FATAL
30
+ 5 => :fatal # Logger::UNKNOWN (treat as fatal)
31
+ }.freeze
32
+
33
+ # Check if a log message should be logged based on severity and logger path
34
+ # @param severity [Integer] Logger severity level (0-5 for SemanticLogger, 0-5 for stdlib Logger)
35
+ # @param path [String] Logger path/name
36
+ # @return [Boolean] true if the message should be logged
37
+ def should_log?(severity, path)
38
+ configured_level = get_log_level(path)
39
+ configured_severity = SEMANTIC_LOGGER_LEVELS[configured_level]
40
+ severity >= configured_severity
41
+ end
42
+
43
+ # SemanticLogger filter integration
44
+ # @param log [SemanticLogger::Log] The log entry to filter
45
+ # @return [Boolean] true if the log should be output
46
+ # Note: This method requires semantic_logger gem to be installed
47
+ def semantic_filter(log)
48
+ unless defined?(SemanticLogger)
49
+ LOG.warn "semantic_filter called but SemanticLogger is not loaded. Install the 'semantic_logger' gem to use this feature."
50
+ return true # Allow all logs through if SemanticLogger is not available
51
+ end
52
+
53
+ class_path = class_path_name(log.name)
54
+ level = SemanticLogger::Levels.index(log.level)
55
+ log.named_tags.merge!({ path: class_path })
56
+ should_log?(level, class_path)
57
+ end
58
+
59
+ # Returns a formatter proc for use with Ruby stdlib Logger
60
+ # Usage:
61
+ # logger = Logger.new($stdout)
62
+ # logger.formatter = client.log_level_client.stdlib_formatter('MyApp')
63
+ # @param logger_name [String] The name/path of the logger
64
+ # @return [Proc] A formatter proc that respects dynamic log levels
65
+ def stdlib_formatter(logger_name)
66
+ proc do |severity, datetime, progname, msg|
67
+ # Convert Logger severity string to integer (DEBUG=0, INFO=1, WARN=2, ERROR=3, FATAL=4)
68
+ severity_int = case severity
69
+ when 'DEBUG' then 0
70
+ when 'INFO' then 1
71
+ when 'WARN' then 2
72
+ when 'ERROR' then 3
73
+ when 'FATAL', 'UNKNOWN' then 4
74
+ else 1
75
+ end
76
+
77
+ # Check if we should log this message
78
+ if should_log?(severity_int, logger_name)
79
+ # Default formatting
80
+ "[#{datetime.strftime('%Y-%m-%d %H:%M:%S.%L')}] #{severity} -- #{progname}: #{msg}\n"
81
+ else
82
+ nil # Don't output the log
83
+ end
84
+ end
85
+ end
86
+
87
+ # Get the log level for a given logger name
88
+ # Returns a LogLevel symbol (:trace, :debug, :info, :warn, :error, :fatal)
89
+ # Defaults to :debug if no config is found
90
+ def get_log_level(logger_name)
91
+ logger_key = @base_client.options.logger_key
92
+
93
+ # If logger key is explicitly set to nil or empty, return default
94
+ if logger_key.nil? || logger_key.empty?
95
+ LOG.debug "logger_key is nil or empty, returning default log level DEBUG"
96
+ return Reforge::LogLevel::DEBUG
97
+ end
98
+
99
+ # Create the evaluation context
100
+ context = {
101
+ "reforge-sdk-logging" => {
102
+ "lang" => "ruby",
103
+ "logger-path" => logger_name
104
+ }
105
+ }
106
+
107
+ # Get the raw config to check its type first
108
+ raw_config = @base_client.config_client.resolver.raw(logger_key)
109
+
110
+ if raw_config.nil?
111
+ LOG.debug "No raw config found for key '#{logger_key}', returning default DEBUG"
112
+ return Reforge::LogLevel::DEBUG
113
+ end
114
+
115
+ # Verify it's a LOG_LEVEL_V2 config
116
+ if raw_config.config_type != :LOG_LEVEL_V2
117
+ LOG.warn "Config '#{logger_key}' is not a LOG_LEVEL_V2 config (type: #{raw_config.config_type}), returning default DEBUG"
118
+ return Reforge::LogLevel::DEBUG
119
+ end
120
+
121
+ begin
122
+ # Evaluate the config with the context
123
+ evaluation = @base_client.config_client.send(:_get, logger_key, context)
124
+
125
+ if evaluation.nil?
126
+ LOG.debug "No log level config found for key '#{logger_key}', returning default DEBUG"
127
+ return Reforge::LogLevel::DEBUG
128
+ end
129
+
130
+ # Get the unwrapped value - this returns a PrefabProto::LogLevel enum value
131
+ proto_log_level = evaluation.report_and_return(@base_client.evaluation_summary_aggregator)
132
+
133
+ # Convert the proto LogLevel to our public LogLevel enum
134
+ Reforge::LogLevel.from_proto(proto_log_level)
135
+ rescue => e
136
+ LOG.warn "Error getting log level for '#{logger_name}': #{e.message}"
137
+ LOG.debug e.backtrace.join("\n")
138
+ Reforge::LogLevel::DEBUG
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ # Convert a logger class name to a path format
145
+ # e.g., "MyApp::MyClass" becomes "my_app.my_class"
146
+ def class_path_name(class_name)
147
+ begin
148
+ log_class = Object.const_get(class_name)
149
+ if log_class.respond_to?(:superclass) && log_class.superclass != Object
150
+ underscore("#{log_class.superclass.name}.#{log_class.name}")
151
+ else
152
+ underscore(log_class.name.to_s)
153
+ end.gsub(/[^a-z_]/i, '.')
154
+ rescue NameError
155
+ # If we can't resolve the constant, just underscore the name
156
+ underscore(class_name.to_s).gsub(/[^a-z_]/i, '.')
157
+ end
158
+ end
159
+
160
+ # Convert CamelCase to snake_case
161
+ def underscore(string)
162
+ string.gsub(/::/, '/').
163
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
164
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
165
+ tr("-", "_").
166
+ downcase
167
+ end
168
+ end
169
+ end
@@ -18,6 +18,7 @@ module Reforge
18
18
  attr_reader :global_context
19
19
  attr_accessor :is_fork
20
20
  attr_reader :symbolize_json_names
21
+ attr_reader :logger_key
21
22
 
22
23
  module ON_INITIALIZATION_FAILURE
23
24
  RAISE = :raise
@@ -65,7 +66,8 @@ module Reforge
65
66
  x_datafile: nil, # DEPRECATED in favor of `datafile`
66
67
  x_use_local_cache: false,
67
68
  symbolize_json_names: false,
68
- global_context: {}
69
+ global_context: {},
70
+ logger_key: 'log-levels.default'
69
71
  )
70
72
  @sdk_key = sdk_key
71
73
  @namespace = namespace
@@ -90,6 +92,7 @@ module Reforge
90
92
  @is_fork = false
91
93
  @global_context = global_context
92
94
  @symbolize_json_names = symbolize_json_names
95
+ @logger_key = logger_key
93
96
 
94
97
  # defaults that may be overridden by context_upload_mode
95
98
  @collect_shapes = false
@@ -49,6 +49,13 @@ module Reforge
49
49
  end
50
50
 
51
51
  def self.log_filter
52
+ unless defined?(SemanticLogger)
53
+ # SemanticLogger is optional - return a pass-through filter
54
+ # Only log debug message if explicitly enabled
55
+ LOG.debug 'log_filter called but SemanticLogger is not available. Install the semantic_logger gem to use this feature.' if ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'] == 'debug'
56
+ return Proc.new { |log| true } # Pass through all logs
57
+ end
58
+
52
59
  InternalLogger.using_reforge_log_filter!
53
60
  return Proc.new do |log|
54
61
  bootstrap_log_level(log)
@@ -60,6 +67,8 @@ module Reforge
60
67
  end
61
68
 
62
69
  def self.bootstrap_log_level(log)
70
+ return true unless defined?(SemanticLogger)
71
+
63
72
  level = ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'] ? ENV['REFORGE_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'].downcase.to_sym : :warn
64
73
  SemanticLogger::Levels.index(level) <= SemanticLogger::Levels.index(log.level)
65
74
  end
data/lib/sdk-reforge.rb CHANGED
@@ -5,7 +5,12 @@ module Reforge
5
5
  VERSION = File.read(File.dirname(__FILE__) + '/../VERSION').strip
6
6
  end
7
7
 
8
- require 'semantic_logger'
8
+ begin
9
+ require 'semantic_logger'
10
+ rescue LoadError
11
+ # semantic_logger is optional - only needed for dynamic log level filtering
12
+ end
13
+
9
14
  require 'reforge/internal_logger'
10
15
  require 'concurrent/atomics'
11
16
  require 'concurrent'
@@ -49,6 +54,8 @@ require 'reforge/client'
49
54
  require 'reforge/config_client_presenter'
50
55
  require 'reforge/config_client'
51
56
  require 'reforge/feature_flag_client'
57
+ require 'reforge/log_level'
58
+ require 'reforge/log_level_client'
52
59
  require 'reforge/reforge'
53
60
  require 'reforge/murmer3'
54
61
  require 'reforge/javascript_stub'
data/sdk-reforge.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: sdk-reforge 1.11.2 ruby lib
5
+ # stub: sdk-reforge 1.12.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "sdk-reforge".freeze
9
- s.version = "1.11.2"
9
+ s.version = "1.12.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2025-10-07"
14
+ s.date = "2025-10-31"
15
15
  s.description = "Feature Flags, Live Config as a service".freeze
16
16
  s.email = "jeff.dwyer@reforge.com.cloud".freeze
17
17
  s.extra_rdoc_files = [
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  "README.md"
21
21
  ]
22
22
  s.files = [
23
+ ".DS_Store",
23
24
  ".envrc.sample",
24
25
  ".github/CODEOWNERS",
25
26
  ".github/pull_request_template.md",
@@ -73,6 +74,8 @@ Gem::Specification.new do |s|
73
74
  "lib/reforge/internal_logger.rb",
74
75
  "lib/reforge/javascript_stub.rb",
75
76
  "lib/reforge/local_config_parser.rb",
77
+ "lib/reforge/log_level.rb",
78
+ "lib/reforge/log_level_client.rb",
76
79
  "lib/reforge/murmer3.rb",
77
80
  "lib/reforge/options.rb",
78
81
  "lib/reforge/periodic_sync.rb",
@@ -116,6 +119,7 @@ Gem::Specification.new do |s|
116
119
  "test/test_internal_logger.rb",
117
120
  "test/test_javascript_stub.rb",
118
121
  "test/test_local_config_parser.rb",
122
+ "test/test_log_level_client.rb",
119
123
  "test/test_logger_initialization.rb",
120
124
  "test/test_options.rb",
121
125
  "test/test_prefab.rb",
@@ -138,7 +142,6 @@ Gem::Specification.new do |s|
138
142
  s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
139
143
  s.add_runtime_dependency(%q<uuid>.freeze, [">= 0"])
140
144
  s.add_runtime_dependency(%q<activesupport>.freeze, [">= 4"])
141
- s.add_runtime_dependency(%q<semantic_logger>.freeze, ["!= 4.16.0"])
142
145
  s.add_development_dependency(%q<allocation_stats>.freeze, [">= 0"])
143
146
  s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
144
147
  s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
@@ -9,8 +9,11 @@ module CommonHelpers
9
9
  $logs = StringIO.new
10
10
  Reforge::Context.global_context.clear
11
11
  Reforge::Context.default_context.clear
12
- SemanticLogger.add_appender(io: $logs, filter: Reforge.log_filter)
13
- SemanticLogger.sync!
12
+
13
+ if defined?(SemanticLogger)
14
+ SemanticLogger.add_appender(io: $logs, filter: Reforge.log_filter)
15
+ SemanticLogger.sync!
16
+ end
14
17
  end
15
18
 
16
19
  def teardown
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestLogLevelClient < Minitest::Test
6
+ def setup
7
+ super
8
+ @options = Reforge::Options.new(
9
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
10
+ logger_key: 'test.log.level.key'
11
+ )
12
+
13
+ @client = Reforge::Client.new(@options)
14
+ end
15
+
16
+ def test_get_log_level_returns_debug_when_no_config_exists
17
+ log_level = @client.log_level_client.get_log_level('MyApp::MyClass')
18
+ assert_equal :debug, log_level
19
+ end
20
+
21
+ def test_get_log_level_with_log_level_v2_config
22
+ # Create a LOG_LEVEL_V2 config
23
+ config = PrefabProto::Config.new(
24
+ key: 'test.log.level.key',
25
+ id: 1,
26
+ config_type: PrefabProto::ConfigType::LOG_LEVEL_V2,
27
+ value_type: PrefabProto::Config::ValueType::LOG_LEVEL,
28
+ rows: [
29
+ PrefabProto::ConfigRow.new(
30
+ values: [
31
+ PrefabProto::ConditionalValue.new(
32
+ criteria: [
33
+ PrefabProto::Criterion.new(
34
+ operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
35
+ property_name: 'reforge-sdk-logging.logger-path',
36
+ value_to_match: PrefabProto::ConfigValue.new(
37
+ string_list: PrefabProto::StringList.new(values: ['MyApp::DebugClass'])
38
+ )
39
+ )
40
+ ],
41
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::DEBUG)
42
+ ),
43
+ PrefabProto::ConditionalValue.new(
44
+ criteria: [
45
+ PrefabProto::Criterion.new(
46
+ operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
47
+ property_name: 'reforge-sdk-logging.logger-path',
48
+ value_to_match: PrefabProto::ConfigValue.new(
49
+ string_list: PrefabProto::StringList.new(values: ['MyApp::InfoClass'])
50
+ )
51
+ )
52
+ ],
53
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::INFO)
54
+ ),
55
+ # Default case - WARN for everything else
56
+ PrefabProto::ConditionalValue.new(
57
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::WARN)
58
+ )
59
+ ]
60
+ )
61
+ ]
62
+ )
63
+
64
+ # Load the config into the resolver
65
+ @client.config_client.resolver.instance_variable_get(:@config_loader).set(config, :test)
66
+ @client.config_client.resolver.update
67
+
68
+ # Test that we get DEBUG for MyApp::DebugClass
69
+ log_level = @client.log_level_client.get_log_level('MyApp::DebugClass')
70
+ assert_equal :debug, log_level
71
+
72
+ # Test that we get INFO for MyApp::InfoClass
73
+ log_level = @client.log_level_client.get_log_level('MyApp::InfoClass')
74
+ assert_equal :info, log_level
75
+
76
+ # Test that we get WARN for anything else (default)
77
+ log_level = @client.log_level_client.get_log_level('MyApp::OtherClass')
78
+ assert_equal :warn, log_level
79
+ end
80
+
81
+ def test_get_log_level_with_all_log_levels
82
+ # Create a LOG_LEVEL_V2 config with all log levels
83
+ config = PrefabProto::Config.new(
84
+ key: 'test.log.level.key',
85
+ id: 1,
86
+ config_type: PrefabProto::ConfigType::LOG_LEVEL_V2,
87
+ value_type: PrefabProto::Config::ValueType::LOG_LEVEL,
88
+ rows: [
89
+ PrefabProto::ConfigRow.new(
90
+ values: [
91
+ PrefabProto::ConditionalValue.new(
92
+ criteria: [
93
+ PrefabProto::Criterion.new(
94
+ operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
95
+ property_name: 'reforge-sdk-logging.logger-path',
96
+ value_to_match: PrefabProto::ConfigValue.new(
97
+ string_list: PrefabProto::StringList.new(values: ['TraceLogger'])
98
+ )
99
+ )
100
+ ],
101
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::TRACE)
102
+ ),
103
+ PrefabProto::ConditionalValue.new(
104
+ criteria: [
105
+ PrefabProto::Criterion.new(
106
+ operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
107
+ property_name: 'reforge-sdk-logging.logger-path',
108
+ value_to_match: PrefabProto::ConfigValue.new(
109
+ string_list: PrefabProto::StringList.new(values: ['ErrorLogger'])
110
+ )
111
+ )
112
+ ],
113
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::ERROR)
114
+ ),
115
+ PrefabProto::ConditionalValue.new(
116
+ criteria: [
117
+ PrefabProto::Criterion.new(
118
+ operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
119
+ property_name: 'reforge-sdk-logging.logger-path',
120
+ value_to_match: PrefabProto::ConfigValue.new(
121
+ string_list: PrefabProto::StringList.new(values: ['FatalLogger'])
122
+ )
123
+ )
124
+ ],
125
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::FATAL)
126
+ )
127
+ ]
128
+ )
129
+ ]
130
+ )
131
+
132
+ # Load the config into the resolver
133
+ @client.config_client.resolver.instance_variable_get(:@config_loader).set(config, :test)
134
+ @client.config_client.resolver.update
135
+
136
+ # Test all log levels
137
+ assert_equal :trace, @client.log_level_client.get_log_level('TraceLogger')
138
+ assert_equal :error, @client.log_level_client.get_log_level('ErrorLogger')
139
+ assert_equal :fatal, @client.log_level_client.get_log_level('FatalLogger')
140
+ end
141
+
142
+ def test_get_log_level_returns_debug_when_logger_key_is_nil
143
+ options = Reforge::Options.new(
144
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
145
+ logger_key: nil
146
+ )
147
+ client = Reforge::Client.new(options)
148
+
149
+ log_level = client.log_level_client.get_log_level('MyApp::MyClass')
150
+ assert_equal :debug, log_level
151
+ end
152
+
153
+ def test_get_log_level_returns_debug_when_logger_key_is_empty
154
+ options = Reforge::Options.new(
155
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
156
+ logger_key: ''
157
+ )
158
+ client = Reforge::Client.new(options)
159
+
160
+ log_level = client.log_level_client.get_log_level('MyApp::MyClass')
161
+ assert_equal :debug, log_level
162
+ end
163
+
164
+ def test_get_log_level_returns_debug_when_config_is_wrong_type
165
+ # Create a regular CONFIG type instead of LOG_LEVEL_V2
166
+ config = PrefabProto::Config.new(
167
+ key: 'test.log.level.key',
168
+ id: 1,
169
+ config_type: PrefabProto::ConfigType::CONFIG,
170
+ value_type: PrefabProto::Config::ValueType::STRING,
171
+ rows: [
172
+ PrefabProto::ConfigRow.new(
173
+ values: [
174
+ PrefabProto::ConditionalValue.new(
175
+ value: PrefabProto::ConfigValue.new(string: 'not a log level')
176
+ )
177
+ ]
178
+ )
179
+ ]
180
+ )
181
+
182
+ # Load the config into the resolver
183
+ @client.config_client.resolver.instance_variable_get(:@config_loader).set(config, :test)
184
+ @client.config_client.resolver.update
185
+
186
+ log_level = @client.log_level_client.get_log_level('MyApp::MyClass')
187
+ assert_equal :debug, log_level
188
+ assert_logged [/Config 'test.log.level.key' is not a LOG_LEVEL_V2 config/]
189
+ end
190
+
191
+ def test_log_level_enum_from_proto
192
+ assert_equal :trace, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::TRACE)
193
+ assert_equal :debug, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::DEBUG)
194
+ assert_equal :info, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::INFO)
195
+ assert_equal :warn, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::WARN)
196
+ assert_equal :error, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::ERROR)
197
+ assert_equal :fatal, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::FATAL)
198
+ assert_equal :debug, Reforge::LogLevel.from_proto(PrefabProto::LogLevel::NOT_SET_LOG_LEVEL)
199
+ end
200
+
201
+ def test_default_logger_key
202
+ options = Reforge::Options.new(
203
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY
204
+ )
205
+
206
+ assert_equal 'log-levels.default', options.logger_key
207
+ end
208
+
209
+ def test_should_log_with_different_severities
210
+ # Create a LOG_LEVEL_V2 config set to INFO
211
+ config = PrefabProto::Config.new(
212
+ key: 'test.log.level.key',
213
+ id: 1,
214
+ config_type: PrefabProto::ConfigType::LOG_LEVEL_V2,
215
+ value_type: PrefabProto::Config::ValueType::LOG_LEVEL,
216
+ rows: [
217
+ PrefabProto::ConfigRow.new(
218
+ values: [
219
+ PrefabProto::ConditionalValue.new(
220
+ value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::INFO)
221
+ )
222
+ ]
223
+ )
224
+ ]
225
+ )
226
+
227
+ @client.config_client.resolver.instance_variable_get(:@config_loader).set(config, :test)
228
+ @client.config_client.resolver.update
229
+
230
+ # SemanticLogger levels: trace=0, debug=1, info=2, warn=3, error=4, fatal=5
231
+ # With INFO level set, we should log INFO (2) and above, but not DEBUG (1) or TRACE (0)
232
+ assert_equal false, @client.log_level_client.should_log?(0, 'MyApp::MyClass'), 'TRACE should not log'
233
+ assert_equal false, @client.log_level_client.should_log?(1, 'MyApp::MyClass'), 'DEBUG should not log'
234
+ assert_equal true, @client.log_level_client.should_log?(2, 'MyApp::MyClass'), 'INFO should log'
235
+ assert_equal true, @client.log_level_client.should_log?(3, 'MyApp::MyClass'), 'WARN should log'
236
+ assert_equal true, @client.log_level_client.should_log?(4, 'MyApp::MyClass'), 'ERROR should log'
237
+ assert_equal true, @client.log_level_client.should_log?(5, 'MyApp::MyClass'), 'FATAL should log'
238
+ end
239
+
240
+ def test_class_path_name_conversion
241
+ client = @client.log_level_client
242
+
243
+ # Test underscore conversion
244
+ assert_equal 'my_app.my_class', client.send(:class_path_name, 'MyApp::MyClass')
245
+ end
246
+
247
+ def test_underscore
248
+ client = @client.log_level_client
249
+
250
+ assert_equal 'my_app/my_class', client.send(:underscore, 'MyApp::MyClass')
251
+ assert_equal 'html_parser', client.send(:underscore, 'HTMLParser')
252
+ assert_equal 'my_simple_class', client.send(:underscore, 'MySimpleClass')
253
+ end
254
+
255
+ def test_semantic_logger_levels_mapping
256
+ # Verify our SEMANTIC_LOGGER_LEVELS constant matches expectations
257
+ assert_equal 0, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:trace]
258
+ assert_equal 1, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:debug]
259
+ assert_equal 2, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:info]
260
+ assert_equal 3, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:warn]
261
+ assert_equal 4, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:error]
262
+ assert_equal 5, Reforge::LogLevelClient::SEMANTIC_LOGGER_LEVELS[:fatal]
263
+ end
264
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdk-reforge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.2
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-10-07 00:00:00.000000000 Z
10
+ date: 2025-10-31 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby
@@ -113,20 +113,6 @@ dependencies:
113
113
  - - ">="
114
114
  - !ruby/object:Gem::Version
115
115
  version: '4'
116
- - !ruby/object:Gem::Dependency
117
- name: semantic_logger
118
- requirement: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - "!="
121
- - !ruby/object:Gem::Version
122
- version: 4.16.0
123
- type: :runtime
124
- prerelease: false
125
- version_requirements: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "!="
128
- - !ruby/object:Gem::Version
129
- version: 4.16.0
130
116
  - !ruby/object:Gem::Dependency
131
117
  name: allocation_stats
132
118
  requirement: !ruby/object:Gem::Requirement
@@ -220,6 +206,7 @@ extra_rdoc_files:
220
206
  - LICENSE.txt
221
207
  - README.md
222
208
  files:
209
+ - ".DS_Store"
223
210
  - ".envrc.sample"
224
211
  - ".github/CODEOWNERS"
225
212
  - ".github/pull_request_template.md"
@@ -273,6 +260,8 @@ files:
273
260
  - lib/reforge/internal_logger.rb
274
261
  - lib/reforge/javascript_stub.rb
275
262
  - lib/reforge/local_config_parser.rb
263
+ - lib/reforge/log_level.rb
264
+ - lib/reforge/log_level_client.rb
276
265
  - lib/reforge/murmer3.rb
277
266
  - lib/reforge/options.rb
278
267
  - lib/reforge/periodic_sync.rb
@@ -316,6 +305,7 @@ files:
316
305
  - test/test_internal_logger.rb
317
306
  - test/test_javascript_stub.rb
318
307
  - test/test_local_config_parser.rb
308
+ - test/test_log_level_client.rb
319
309
  - test/test_logger_initialization.rb
320
310
  - test/test_options.rb
321
311
  - test/test_prefab.rb