supports collecting metrics and logs (#7)

supports collecting metrics and logs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80a611c..d8655ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
 ==================
 Release Notes.
 
+0.2.0
+------------------
+#### Features
+- Supports collecting metrics and logs.
+
+#### Plugins
+
+#### Documentation
+
 0.1.0
 ------------------
 #### Features
diff --git a/Gemfile b/Gemfile
index 3f5e0e4..993d132 100644
--- a/Gemfile
+++ b/Gemfile
@@ -17,4 +17,8 @@
 
 gemspec name: 'skywalking'
 
-ruby ">= 3.0.0"
\ No newline at end of file
+ruby ">= 3.0.0"
+
+# Optional dependencies for enhanced performance
+# Uncomment the following line to enable FFI for better system call performance
+# gem 'ffi', '~> 1.17'
\ No newline at end of file
diff --git a/docs/en/agent/meter-log-reporter.md b/docs/en/agent/meter-log-reporter.md
new file mode 100644
index 0000000..89128c6
--- /dev/null
+++ b/docs/en/agent/meter-log-reporter.md
@@ -0,0 +1,59 @@
+# Ruby Agent Meter and Log Reporter
+
+The meter reporter feature enables collection and reporting of runtime metrics to the SkyWalking OAP backend.
+
+### Runtime Metrics (Enabled by Default)
+
+The agent automatically collects Ruby runtime metrics when `meter_reporter_active` is enabled (default: true).
+
+#### Collected Runtime Metrics
+
+**CPU Metrics:**
+
+- `instance_ruby_cpu_usage_percent` - Ruby process CPU usage percentage
+
+**Memory Metrics:**
+
+- `instance_ruby_memory_rss_mb` - Ruby process RSS memory usage in MB
+- `instance_ruby_memory_usage_percent` - Ruby process memory usage percentage
+
+**Garbage Collection Metrics:**
+
+- `instance_ruby_gc_count_total` - Total GC execution count
+- `instance_ruby_gc_minor_count_total` - Minor GC count (if available)
+- `instance_ruby_gc_major_count_total` - Major GC count (if available)
+- `instance_ruby_gc_time_total` - Total GC time in milliseconds (cumulative)
+- `instance_ruby_heap_usage_percent` - Heap memory usage percentage
+- `instance_ruby_heap_live_slots_count` - Number of live heap slots
+- `instance_ruby_heap_available_slots_count` - Number of available heap slots
+
+**Thread Metrics:**
+
+- `instance_ruby_thread_count_active` - Number of active threads (all alive threads)
+- `instance_ruby_thread_count_running` - Number of threads in running state
+
+### Log Integration
+
+When log reporter is enabled, the agent automatically:
+
+- **Intercepts Ruby Standard Logger**: Automatically patches the `Logger` class to collect logs
+- **Integrates Trace Context**: Adds trace ID, segment ID, and span ID to log messages when available
+
+#### Supported Logger
+
+Currently, the agent supports:
+
+- **Ruby Standard Logger** (`Logger` class) - Automatically intercepted and collected
+
+#### Log Configuration Options
+
+```ruby
+# Configure log reporter level (default: Logger::INFO)
+config[:log_reporter_level] = Logger::INFO
+
+# Configure log report period in seconds (default: 5)
+config[:log_report_period] = 5
+
+# Configure maximum log queue size (default: 1000)
+config[:max_log_queue_size] = 1000
+```
diff --git a/docs/en/setup/quick-start.md b/docs/en/setup/quick-start.md
index 13670c7..4434057 100644
--- a/docs/en/setup/quick-start.md
+++ b/docs/en/setup/quick-start.md
@@ -33,7 +33,7 @@
 
 If successful, the following will be displayed:
 
-```ruby
+```shell
   Successfully built RubyGem
   Name: skywalking
   Version: <version>
@@ -84,27 +84,39 @@
 Skywalking.start(
   service_name: 'sw-srv',
   instance_name: 'sw-inst',
-  collector_backend_services: 'oap:11800'
+  collector_backend_services: 'oap:11800',
+  meter_reporter_active: true,
+  log_reporter_active: true,
+  meter_report_period: 30,
+  log_report_period: 10
 )
 ~~~
 
 The following is an example of a configuration file:
+
 ~~~yaml
 common: &defaults
   service_name: Ruby-Agent-Common
   log_level: debug
+  meter_reporter_active: true
+  log_reporter_active: true
+  meter_report_period: 20
+  log_report_period: 5
 
 development:
   <<: *defaults
   service_name: Ruby-Agent-Development
+  log_reporter_level: 0  # DEBUG
 
 test:
   <<: *defaults
   service_name: Ruby-Agent-Test
+  log_reporter_level: 1  # INFO
 
 production:
   <<: *defaults
   service_name: Ruby-Agent-Production
+  log_reporter_level: 2  # WARN
 ~~~
 
 The following lists all the configuration options:
@@ -127,3 +139,10 @@
 | collector_heartbeat_period      | SW_AGENT_COLLECTOR_HEARTBEAT_PERIOD      | 30                   | he agent will send heartbeat to OAP every `collector_heartbeat_period` seconds.                                                           |
 | properties_report_period_factor | SW_AGENT_PROPERTIES_REPORT_PERIOD_FACTOR | 10                   | The agent will report service instance properties every `collector_heartbeat_period * properties_report_period_factor` seconds.           |
 | max_queue_size                  | SW_AGENT_MAX_QUEUE_SIZE                  | 10000                | The maximum queue size for reporting data.                                                                                                |
+| meter_reporter_active           | SW_AGENT_METER_REPORTER_ACTIVE           | true                 | Enable/disable meter reporter for runtime metrics collection.                                                                             |
+| meter_report_period             | SW_AGENT_METER_REPORT_PERIOD             | 60                   | Meter report period in seconds.                                                                                                           |
+| max_meter_queue_size            | SW_AGENT_MAX_METER_QUEUE_SIZE            | 1000                 | Maximum meter queue size for buffering metrics data.                                                                                      |
+| log_reporter_active             | SW_AGENT_LOG_REPORTER_ACTIVE             | true                 | Enable/disable log reporter for log collection.                                                                                           |
+| log_reporter_level              | SW_AGENT_LOG_REPORTER_LEVEL              | 1 (INFO)             | Minimum log level to report (Logger::DEBUG=0, INFO=1, WARN=2, ERROR=3, FATAL=4).                                                          |
+| log_report_period               | SW_AGENT_LOG_REPORT_PERIOD               | 5                    | Log report period in seconds.                                                                                                             |
+| max_log_queue_size              | SW_AGENT_MAX_LOG_QUEUE_SIZE              | 1000                 | Maximum log queue size for buffering log data.                                                                                            |
diff --git a/docs/menu.yml b/docs/menu.yml
index 2fa797f..c283149 100644
--- a/docs/menu.yml
+++ b/docs/menu.yml
@@ -23,6 +23,8 @@
     catalog:
       - name: Supported Plugins
         path: /en/agent/plugins
+      - name: Meter and Log Report
+        path: /en/agent/meter-and-log-report
   - name: Development and Contribution
     catalog:
       - name: How to Release
diff --git a/lib/skywalking/agent.rb b/lib/skywalking/agent.rb
index 2c61272..977de01 100644
--- a/lib/skywalking/agent.rb
+++ b/lib/skywalking/agent.rb
@@ -17,6 +17,7 @@
 require_relative 'environment'
 require_relative 'plugins_manager'
 require_relative 'reporter/report'
+require_relative 'reporter/log_buffer_trigger'
 require_relative 'tracing/span_context'
 require_relative 'tracing/carrier_item'
 require_relative 'tracing/segment'
@@ -62,13 +63,21 @@
       end
 
       attr_reader :logger, :agent_config
+
+      # Get the singleton instance
+      # @return [Agent, nil] the agent instance or nil if not started
+      def instance
+        @agent
+      end
     end
 
-    attr_reader :plugins, :reporter
+    attr_reader :plugins, :reporter, :log_buffer, :config
 
     def initialize(config)
+      @config = config
       @plugins = Plugins::PluginsManager.new(config)
       @reporter = Reporter::Report.new(config)
+      @log_buffer = Reporter::LogBufferTrigger.new(config)
 
       add_shutdown_hook
     end
@@ -80,6 +89,9 @@
     def start!
       @plugins.init_plugins
       @reporter.init_reporter
+      # Start log reporting thread
+      start_log_reporting_thread if @config[:log_reporter_active]
+
       self
     end
 
@@ -94,5 +106,46 @@
         shutdown
       end
     end
+
+    # Check if log reporter is active
+    # @return [Boolean] true if log reporter is active
+    def log_reporter_active?
+      @config[:log_reporter_active]
+    end
+
+    private
+
+    # Start the log reporting thread
+    def start_log_reporting_thread
+      Thread.new do
+        loop do
+          break unless log_reporter_active?
+
+          process_log_queue
+          sleep @config[:log_report_period]
+        end
+      end
+    end
+
+    # Process the log queue and send data to the server
+    def process_log_queue
+      log_count = 0
+      enumerator = Enumerator.new do |yielder|
+        while (log_data = log_buffer.stream_data)
+          log_data.each do |data|
+            log_count += 1
+            yielder << data
+          end
+        end
+      end
+
+      enumerator.each_slice(10) do |batch|
+        begin
+          reporter.report_log(batch)
+        rescue => e
+          Agent.logger.warn "Failed to report log data: #{e.message}"
+        end
+      end
+    end
   end
 end
diff --git a/lib/skywalking/configuration.rb b/lib/skywalking/configuration.rb
index 6efe3ae..0e4dc16 100644
--- a/lib/skywalking/configuration.rb
+++ b/lib/skywalking/configuration.rb
@@ -100,6 +100,46 @@
         default: 10000,
         desc: 'The maximum queue size for reporting data'
       },
+      :meter_reporter_active => {
+        type: :bool,
+        default: true,
+        desc: 'Enable meter reporter'
+      },
+      :runtime_meter_reporter_active => {
+        type: :bool,
+        default: true,
+        desc: 'Enable Ruby runtime meter reporter'
+      },
+      :meter_report_period => {
+        type: :int,
+        default: 60,
+        desc: 'Meter report period in seconds'
+      },
+      :max_meter_queue_size => {
+        type: :int,
+        default: 1000,
+        desc: 'Maximum meter queue size'
+      },
+      :log_reporter_active => {
+        type: :bool,
+        default: true,
+        desc: 'Enable log reporter'
+      },
+      :log_reporter_level => {
+        type: :int,
+        default: Logger::INFO,
+        desc: 'Minimum log level to report (Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL)'
+      },
+      :log_report_period => {
+        type: :int,
+        default: 5,
+        desc: 'Log report period in seconds'
+      },
+      :max_log_queue_size => {
+        type: :int,
+        default: 1000,
+        desc: 'Maximum log queue size'
+      }
     }.freeze
 
     # @api private
@@ -173,18 +213,18 @@
         next if env_value.nil?
 
         type = env_schema[:type]
-        case type
-        when :string
-          new_config[env_key] = env_value.to_s
-        when :bool
-          # rubocop:disable Performance/CollectionLiteralInLoop
-          new_config[env_key] = !%w[0 false].include?(env_value.strip.downcase)
-          # rubocop:enable Performance/CollectionLiteralInLoop
-        when :int
-          new_config[env_key] = env_value.to_s
-        else
-          env_value
-        end
+        new_config[env_key] = case type
+                              when :string
+                                env_value.to_s
+                              when :bool
+                                # rubocop:disable Performance/CollectionLiteralInLoop
+                                !%w[0 false].include?(env_value.strip.downcase)
+                              # rubocop:enable Performance/CollectionLiteralInLoop
+                              when :int
+                                env_value.to_i
+                              else
+                                env_value
+                              end
       end
 
       new_config
diff --git a/lib/skywalking/environment.rb b/lib/skywalking/environment.rb
index aa914e2..8fa1efd 100644
--- a/lib/skywalking/environment.rb
+++ b/lib/skywalking/environment.rb
@@ -37,7 +37,7 @@
       rescue
         nil
       end
-      
+
       def env
         ::Rails.env
       end
@@ -63,7 +63,7 @@
       rescue
         "Sinatra"
       end
-      
+
       def env
         ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
       end
@@ -81,7 +81,7 @@
       def app_name
         "Ruby"
       end
-      
+
       def env
         ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
       end
@@ -111,7 +111,7 @@
                             "."
                           end
     end
-    
+
     def framework_env
       @framework_env ||= framework_info.env
     end
diff --git a/lib/skywalking/meter.rb b/lib/skywalking/meter.rb
new file mode 100644
index 0000000..7673fc5
--- /dev/null
+++ b/lib/skywalking/meter.rb
@@ -0,0 +1,29 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative 'meter/base'
+require_relative 'meter/meter_service'
+require_relative 'meter/runtime/cpu_data_source'
+require_relative 'meter/runtime/mem_data_source'
+require_relative 'meter/runtime/gc_data_source'
+require_relative 'meter/runtime/thread_data_source'
+
+module Skywalking
+  # Main module for meter functionality
+  module Meter
+    # Classes are already defined through require statements above
+    # No need to reassign them to themselves
+  end
+end
diff --git a/lib/skywalking/meter/base.rb b/lib/skywalking/meter/base.rb
new file mode 100644
index 0000000..fac3f2b
--- /dev/null
+++ b/lib/skywalking/meter/base.rb
@@ -0,0 +1,79 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative '../reporter/client/proto'
+
+module Skywalking
+  module Meter
+    # Base class for all data sources
+    class DataSource
+      # Automatically register all generator methods as gauges
+      # @param meter_service [MeterService] the service to register gauges with
+      def register(meter_service)
+        methods.grep(/_generator$/).each do |method_name|
+          metric_name = "instance_ruby_#{method_name.to_s.sub('_generator', '')}"
+          # Create a lambda that calls the generator method
+          getter = lambda { send(method_name) }
+          gauge = Gauge.new(metric_name, getter)
+          meter_service.register(gauge)
+        end
+      end
+    end
+
+    # Represents a gauge metric that reports instantaneous values
+    class Gauge
+      attr_reader :name
+
+      # @param name [String] metric name
+      # @param getter [Proc] a callable that returns the current value
+      def initialize(name, getter)
+        @name = name
+        @getter = getter
+        @labels = []
+      end
+
+      # Add a label to this gauge
+      # @param key [String] label key
+      # @param value [String] label value
+      # @return [self]
+      def add_label(key, value)
+        @labels << Label.new(name: key, value: value)
+        self
+      end
+
+      # Collect current metric value
+      # @return [MeterData] meter data
+      def collect
+        value = @getter.call
+        MeterData.new(
+          singleValue: MeterSingleValue.new(
+            name: @name,
+            value: value.to_f,
+            labels: @labels
+          )
+        )
+      rescue
+        # Return zero value if getter fails
+        MeterData.new(
+          singleValue: MeterSingleValue.new(
+            name: @name,
+            value: 0.0,
+            labels: @labels
+          )
+        )
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/meter_service.rb b/lib/skywalking/meter/meter_service.rb
new file mode 100644
index 0000000..0d8eff7
--- /dev/null
+++ b/lib/skywalking/meter/meter_service.rb
@@ -0,0 +1,99 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+module Skywalking
+  module Meter
+    # Service to manage metric collection and reporting
+    class MeterService
+      include Log::Logging
+
+      # @param config [Hash] configuration
+      # @param meter_trigger [MeterBufferTrigger] buffer trigger for queuing data
+      def initialize(config, meter_trigger)
+        @config = config
+        @gauges = []
+        @meter_trigger = meter_trigger
+        @running = false
+        @mutex = Mutex.new
+        @collector_thread = nil
+      end
+
+      # Register a gauge for collection
+      # @param gauge [Gauge] the gauge to register
+      def register(gauge)
+        @mutex.synchronize do
+          @gauges << gauge
+        end
+      end
+
+      # Start the meter collection service
+      def start
+        return if @running
+
+        @running = true
+
+        @collector_thread = Thread.new do
+          Thread.current.name = 'MeterCollector'
+          run_collection_loop
+        end
+      end
+
+      # Stop the meter collection service
+      def stop
+        return unless @running
+
+        @running = false
+        @collector_thread&.join(5)
+      end
+
+      private
+
+      def run_collection_loop
+        period = @config[:meter_report_period]
+
+        while @running
+          begin
+            collect_and_queue_metrics
+            sleep period
+          rescue => e
+            error "Error in meter collection loop: #{e.message}"
+            error e.backtrace.join("\n")
+            sleep period
+          end
+        end
+      end
+
+      def collect_and_queue_metrics
+        @mutex.synchronize do
+          collected_count = 0
+          @gauges.each do |gauge|
+            begin
+              meter_data = gauge.collect
+              if meter_data
+                meter_data.service = @config[:service_name]
+                meter_data.serviceInstance = @config[:instance_name]
+                meter_data.timestamp = (Time.now.to_f * 1000).to_i
+                @meter_trigger << meter_data
+                collected_count += 1
+              end
+            rescue => e
+              warn "Error collecting gauge #{gauge.name}: #{e.message}"
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/runtime/cpu_data_source.rb b/lib/skywalking/meter/runtime/cpu_data_source.rb
new file mode 100644
index 0000000..29269bc
--- /dev/null
+++ b/lib/skywalking/meter/runtime/cpu_data_source.rb
@@ -0,0 +1,57 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative '../base'
+
+module Skywalking
+  module Meter
+    module Runtime
+      # DataSource for collecting Ruby process CPU usage metrics
+      class CpuDataSource < DataSource
+        def initialize
+          @last_cpu_time = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
+          @last_wall_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+        end
+
+        # Ruby process CPU usage percentage
+        def cpu_usage_percent_generator
+          begin
+            current_cpu_time = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
+            current_wall_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+            cpu_time_diff = current_cpu_time - @last_cpu_time
+            wall_time_diff = current_wall_time - @last_wall_time
+
+            # Calculate CPU usage as percentage of wall time
+            if wall_time_diff > 0
+              cpu_usage = (cpu_time_diff / wall_time_diff) * 100.0
+              cpu_usage = [cpu_usage, 0.0].max
+            else
+              cpu_usage = 0.0
+            end
+
+            # Update last values for next calculation
+            @last_cpu_time = current_cpu_time
+            @last_wall_time = current_wall_time
+
+            cpu_usage
+          rescue
+            0.0
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/runtime/gc_data_source.rb b/lib/skywalking/meter/runtime/gc_data_source.rb
new file mode 100644
index 0000000..428a709
--- /dev/null
+++ b/lib/skywalking/meter/runtime/gc_data_source.rb
@@ -0,0 +1,99 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative '../base'
+
+module Skywalking
+  module Meter
+    module Runtime
+      # DataSource for collecting GC metrics
+      class GcDataSource < DataSource
+        def initialize
+          @cached_stats = nil
+          @cached_count = nil
+          @cache_time = 0
+          @cache_duration = 60
+        end
+
+        # Total GC count
+        def gc_count_total_generator
+          get_gc_data
+          @cached_count || 0
+        rescue
+          0
+        end
+
+        # Minor GC count (if available)
+        def gc_minor_count_total_generator
+          stats = get_gc_stats
+          stats[:minor_gc_count] || 0
+        rescue
+          0
+        end
+
+        # Major GC count (if available)
+        def gc_major_count_total_generator
+          stats = get_gc_stats
+          stats[:major_gc_count] || 0
+        rescue
+          0
+        end
+
+        # GC total time
+        def gc_time_total_generator
+          stats = get_gc_stats
+          stats[:time] || 0
+        rescue
+          0
+        end
+
+        # Heap usage percentage
+        def heap_usage_percent_generator
+          stats = get_gc_stats
+          if stats[:heap_available_slots] && stats[:heap_available_slots] > 0
+            (stats[:heap_live_slots].to_f / stats[:heap_available_slots] * 100)
+          else
+            0.0
+          end
+        rescue
+          0.0
+        end
+
+        private
+
+        # Get cached GC statistics, refresh if cache is expired
+        def get_gc_stats
+          current_time = Time.now.to_i
+          if @cached_stats.nil? || (current_time - @cache_time) > @cache_duration
+            @cached_stats = GC.stat
+            @cache_time = current_time
+          end
+          @cached_stats
+        end
+
+        # Get both GC.count and GC.stat data, refresh if cache is expired
+        def get_gc_data
+          current_time = Time.now.to_i
+          if @cached_stats.nil? || (current_time - @cache_time) > @cache_duration
+            @cached_stats = GC.stat
+            @cached_count = GC.count
+            @cache_time = current_time
+          end
+          @cached_stats
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/runtime/mem_data_source.rb b/lib/skywalking/meter/runtime/mem_data_source.rb
new file mode 100644
index 0000000..3aba09f
--- /dev/null
+++ b/lib/skywalking/meter/runtime/mem_data_source.rb
@@ -0,0 +1,137 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require 'get_process_mem'
+require_relative '../base'
+
+module Skywalking
+  module Meter
+    module Runtime
+      # DataSource for collecting memory usage metrics
+      class MemDataSource < DataSource
+        def initialize
+          @process_mem = GetProcessMem.new
+          @cached_total_memory = nil
+          @cache_time = 0
+          @cache_duration = 60
+          @ffi_loaded = false
+          load_ffi_if_available
+        end
+
+        # Process RSS (Resident Set Size) in MB
+        def memory_rss_mb_generator
+          @process_mem.bytes / (1024.0 * 1024.0)
+        rescue
+          0.0
+        end
+
+        # Process memory usage percentage
+        def memory_usage_percent_generator
+          rss_bytes = @process_mem.bytes
+          total_memory = get_total_memory
+
+          if total_memory && total_memory > 0
+            (rss_bytes.to_f / total_memory * 100).round(2)
+          end
+        rescue
+          nil
+        end
+
+        private
+
+        # Load FFI if available for more efficient system calls
+        def load_ffi_if_available
+          require 'ffi'
+          @ffi_loaded = true
+        rescue LoadError
+          @ffi_loaded = false
+        end
+
+        # Get total system memory with caching to avoid frequent system calls
+        def get_total_memory
+          current_time = Time.now.to_i
+
+          if @cached_total_memory.nil? || (current_time - @cache_time) > @cache_duration
+            @cached_total_memory = fetch_total_memory
+            @cache_time = current_time
+          end
+
+          @cached_total_memory
+        end
+
+        # Fetch total memory using platform-specific methods
+        def fetch_total_memory
+          if RUBY_PLATFORM.include?('linux')
+            fetch_linux_total_memory
+          elsif RUBY_PLATFORM.include?('darwin')
+            fetch_macos_total_memory
+          end
+        end
+
+        # Fetch total memory on Linux using /proc/meminfo
+        def fetch_linux_total_memory
+          meminfo = File.read('/proc/meminfo')
+          total = (meminfo.match(/MemTotal:\s+(\d+)/)&.captures&.first || '0').to_i * 1024
+          total > 0 ? total : nil
+        rescue
+          nil
+        end
+
+        # Fetch total memory on macOS using sysctl
+        def fetch_macos_total_memory
+          if @ffi_loaded
+            result = fetch_macos_total_memory_ffi
+            return result if result
+          end
+
+          fetch_macos_total_memory_sysctl
+        end
+
+        # Use FFI to call sysctl directly
+        def fetch_macos_total_memory_ffi
+          extend FFI::Library
+          ffi_lib 'c'
+
+          attach_function :sysctl, [:pointer, :uint, :pointer, :pointer, :pointer, :size_t], :int
+
+          # sysctlnametomib for hw.memsize
+          mib = FFI::MemoryPointer.new(:int, 2)
+          mib.put_int(0, 6) # CTL_HW
+          mib.put_int(4, 24) # HW_MEMSIZE
+
+          size = FFI::MemoryPointer.new(:size_t)
+          size.put_size_t(0, 8)
+
+          result = FFI::MemoryPointer.new(:uint64_t)
+
+          if sysctl(mib, 2, result, size, nil, 0) == 0
+            value = result.read_uint64
+            value > 0 ? value : nil
+          end
+        rescue
+          nil
+        end
+
+        # Fallback to sysctl command (still forks but cached)
+        def fetch_macos_total_memory_sysctl
+          result = `sysctl -n hw.memsize 2>/dev/null`.strip.to_i
+          result > 0 ? result : nil
+        rescue
+          nil
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/runtime/ruby_runtime_data_source.rb b/lib/skywalking/meter/runtime/ruby_runtime_data_source.rb
new file mode 100644
index 0000000..23daf13
--- /dev/null
+++ b/lib/skywalking/meter/runtime/ruby_runtime_data_source.rb
@@ -0,0 +1,59 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative '../base'
+
+module Skywalking
+  module Meter
+    module Runtime
+      # Enhanced Ruby-specific runtime metrics
+      class RubyRuntimeDataSource < DataSource
+        def initialize
+          @cached_stats = nil
+          @cache_time = 0
+          @cache_duration = 60
+        end
+
+        # Heap live slots count - important for memory pressure
+        def heap_live_slots_count_generator
+          stats = get_gc_stats
+          stats[:heap_live_slots] || 0
+        rescue
+          0
+        end
+
+        # Heap available slots count - total capacity
+        def heap_available_slots_count_generator
+          stats = get_gc_stats
+          stats[:heap_available_slots] || 0
+        rescue
+          0
+        end
+
+        private
+
+        # Get cached GC statistics, refresh if cache is expired
+        def get_gc_stats
+          current_time = Time.now.to_i
+          if @cached_stats.nil? || (current_time - @cache_time) > @cache_duration
+            @cached_stats = GC.stat
+            @cache_time = current_time
+          end
+          @cached_stats
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/meter/runtime/thread_data_source.rb b/lib/skywalking/meter/runtime/thread_data_source.rb
new file mode 100644
index 0000000..35549f7
--- /dev/null
+++ b/lib/skywalking/meter/runtime/thread_data_source.rb
@@ -0,0 +1,39 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative '../base'
+
+module Skywalking
+  module Meter
+    module Runtime
+      # DataSource for collecting thread metrics
+      class ThreadDataSource < DataSource
+        # Active thread count (alive threads)
+        def thread_count_active_generator
+          Thread.list.count(&:alive?)
+        rescue
+          0
+        end
+
+        # Running thread count (threads in run state)
+        def thread_count_running_generator
+          Thread.list.count { |t| t.alive? && t.status == "run" }
+        rescue
+          0
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/plugins/logger.rb b/lib/skywalking/plugins/logger.rb
new file mode 100644
index 0000000..b865f2a
--- /dev/null
+++ b/lib/skywalking/plugins/logger.rb
@@ -0,0 +1,168 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require 'logger'
+
+module Skywalking
+  module Plugins
+    # Logger plugin intercepts Ruby's standard Logger to collect logs
+    class Logger < PluginsManager::SWPlugin
+      module LoggerIntercept
+        # Thread-local flag to prevent recursive log collection
+        COLLECTING_LOG_KEY = :skywalking_collecting_log
+
+        # Map Logger severity levels to string names
+        SEVERITY_NAMES = {
+          ::Logger::DEBUG => 'DEBUG',
+          ::Logger::INFO => 'INFO',
+          ::Logger::WARN => 'WARN',
+          ::Logger::ERROR => 'ERROR',
+          ::Logger::FATAL => 'FATAL',
+          ::Logger::UNKNOWN => 'UNKNOWN'
+        }.freeze
+
+        # Map severity names to levels
+        SEVERITY_LEVELS = {
+          'DEBUG' => ::Logger::DEBUG,
+          'INFO' => ::Logger::INFO,
+          'WARN' => ::Logger::WARN,
+          'ERROR' => ::Logger::ERROR,
+          'FATAL' => ::Logger::FATAL,
+          'UNKNOWN' => ::Logger::UNKNOWN
+        }.freeze
+
+        # Override the add method to intercept log calls
+        # @param severity [Integer] log severity level
+        # @param message [String, nil] log message
+        # @param progname [String, nil] program name
+        # @param block [Proc, nil] block that returns message
+        def add(severity, message = nil, progname = nil, &block)
+          # Call original method first
+          result = super
+
+          # Skip if we're already collecting logs to prevent recursion
+          return result if Thread.current[COLLECTING_LOG_KEY]
+
+          # Skip if log reporter is not active
+          agent = Agent.instance
+          return result unless agent&.log_reporter_active?
+
+          # Check severity threshold
+          configured_level = agent.config[:log_reporter_level]
+          min_level = if configured_level.is_a?(String)
+                        SEVERITY_LEVELS[configured_level.upcase] || ::Logger::INFO
+                      else
+                        configured_level || ::Logger::INFO
+                      end
+
+          return result unless severity >= min_level
+
+          # Set flag to prevent recursion
+          Thread.current[COLLECTING_LOG_KEY] = true
+          begin
+            collect_log_data(severity, message, progname, &block)
+          rescue => e
+            agent.logger.warn("SkyWalking log collection error: #{e.message}") if agent.config[:debug_mode]
+          ensure
+            # Always clear the flag
+            Thread.current[COLLECTING_LOG_KEY] = false
+          end
+
+          result
+        end
+
+        private
+
+        # Collect log data and send to SkyWalking
+        # @param severity [Integer] log severity level
+        # @param message [String, nil] log message
+        # @param progname [String, nil] program name
+        # @param block [Proc, nil] block that returns message
+        def collect_log_data(severity, message, progname, &block)
+          # Format the message
+          msg = if message.nil?
+                  if block_given?
+                    yield
+                  else
+                    progname
+                  end
+                else
+                  message
+                end
+
+          return if msg.nil?
+
+          # Get current context
+          begin
+            context = Tracing::ContextManager.current_context
+            trace_context = if context && !context.is_a?(Tracing::IgnoredContext) && context.segment
+                              segment = context.segment
+                              span = context.active_span
+
+                              V3::TraceContext.new(
+                                traceId: segment.related_traces.first&.to_s,
+                                traceSegmentId: segment.segment_id.to_s,
+                                spanId: span ? span.span_id : -1
+                              )
+                            end
+          rescue
+            # If tracing context is not available, continue without it
+            trace_context = nil
+          end
+
+          # Create log data
+          agent = Agent.instance
+          return unless agent # Safety check
+
+          log_data = V3::LogData.new(
+            timestamp: (Time.now.to_f * 1000).to_i,
+            service: agent.config[:service_name],
+            serviceInstance: agent.config[:instance_name],
+            endpoint: context&.active_span&.operation || '',
+            body: V3::LogDataBody.new(
+              type: 'text',
+              text: V3::TextLog.new(text: msg.to_s)
+            ),
+            traceContext: trace_context,
+            tags: V3::LogTags.new(
+              data: [
+                V3::KeyStringValuePair.new(key: 'level', value: SEVERITY_NAMES[severity] || 'UNKNOWN'),
+                V3::KeyStringValuePair.new(key: 'logger', value: progname || self.class.name),
+                V3::KeyStringValuePair.new(key: 'thread', value: Thread.current.name || Thread.current.object_id.to_s)
+              ]
+            ),
+            layer: 'GENERAL'
+          )
+
+          # Send to log buffer
+          agent.log_buffer << log_data
+        end
+      end
+
+      # Check if the plugin can be installed
+      # @return [Boolean] true if Logger is defined
+      def plugin_valid?
+        defined?(::Logger)
+      end
+
+      # Install the plugin by prepending the intercept module
+      def install
+        ::Logger.prepend LoggerIntercept
+      end
+
+      register :logger
+    end
+  end
+end
diff --git a/lib/skywalking/reporter/client/grpc_client.rb b/lib/skywalking/reporter/client/grpc_client.rb
index 2d32df1..a87e8a8 100644
--- a/lib/skywalking/reporter/client/grpc_client.rb
+++ b/lib/skywalking/reporter/client/grpc_client.rb
@@ -33,10 +33,11 @@
 
           def report_instance_properties
             begin
+              properties = gen_service_instance
               req = InstanceProperties.new(
                 service: @config[:service_name],
                 serviceInstance: @config[:instance_name],
-                properties: gen_service_instance
+                properties: properties
               )
 
               @management_service.report_instance_properties(req)
@@ -108,6 +109,49 @@
             error "Error to report trace segment: #{e.message}}"
           end
         end
+
+        class MeterReportServiceGrpc
+          include Log::Logging
+
+          def initialize(config)
+            @config = config
+            @meter_service ||= MeterReportServiceStub.new(
+              @config[:collector_backend_services],
+              :this_channel_is_insecure
+            )
+          end
+
+          def report_meter(enumerator)
+            @meter_service.collect(enumerator)
+          rescue Exception => e
+            error "Error to report meter data: #{e.message}"
+          end
+        end
+
+        class LogReportServiceGrpc
+          include Log::Logging
+
+          def initialize(config)
+            @config = config
+            @log_service ||= V3::LogReportService::Stub.new(
+              @config[:collector_backend_services],
+              :this_channel_is_insecure
+            )
+          end
+
+          def report_log(log_data_array)
+            return if log_data_array.nil? || log_data_array.empty?
+
+            # Create an enumerator that yields log data
+            enumerator = Enumerator.new do |yielder|
+              log_data_array.each { |log_data| yielder << log_data }
+            end
+
+            @log_service.collect(enumerator)
+          rescue Exception => e
+            error "Error to report log data: #{e.message}"
+          end
+        end
       end
     end
   end
diff --git a/lib/skywalking/reporter/client/proto.rb b/lib/skywalking/reporter/client/proto.rb
index de1c639..2f3b6bf 100644
--- a/lib/skywalking/reporter/client/proto.rb
+++ b/lib/skywalking/reporter/client/proto.rb
@@ -16,6 +16,10 @@
 require_relative '../../proto/management/Management_services_pb'
 require_relative '../../proto/language-agent/Tracing_services_pb'
 require_relative '../../proto/language-agent/Tracing_pb'
+require_relative '../../proto/language-agent/Meter_services_pb'
+require_relative '../../proto/language-agent/Meter_pb'
+require_relative '../../proto/logging/Logging_services_pb'
+require_relative '../../proto/logging/Logging_pb'
 
 module Skywalking
   ManagementServiceStub = Skywalking::V3::ManagementService::Stub
@@ -26,4 +30,10 @@
   SpanObject = Skywalking::V3::SpanObject
   SegmentReference = Skywalking::V3::SegmentReference
   KeyStringValuePair = Skywalking::V3::KeyStringValuePair
-end
\ No newline at end of file
+  MeterReportServiceStub = Skywalking::V3::MeterReportService::Stub
+  MeterData = Skywalking::V3::MeterData
+  MeterSingleValue = Skywalking::V3::MeterSingleValue
+  MeterHistogram = Skywalking::V3::MeterHistogram
+  MeterBucketValue = Skywalking::V3::MeterBucketValue
+  Label = Skywalking::V3::Label
+end
diff --git a/lib/skywalking/reporter/grpc.rb b/lib/skywalking/reporter/grpc.rb
index db0ee4f..fcd6c3c 100644
--- a/lib/skywalking/reporter/grpc.rb
+++ b/lib/skywalking/reporter/grpc.rb
@@ -23,6 +23,8 @@
         @cfg = config
         @ms_client = Skywalking::Reporter::Client::GrpcClient::ManagementServiceGrpc.new(config)
         @trace_client = Skywalking::Reporter::Client::GrpcClient::TraceSegmentReportServiceGrpc.new(config)
+        @meter_client = Skywalking::Reporter::Client::GrpcClient::MeterReportServiceGrpc.new(config)
+        @log_client = Skywalking::Reporter::Client::GrpcClient::LogReportServiceGrpc.new(config)
         @send_properties_counter = 0
         @counter_mutex = Mutex.new
       end
@@ -41,6 +43,14 @@
       def report_segment(enumerator)
         @trace_client.report_segment(enumerator)
       end
+
+      def report_meter(enumerator)
+        @meter_client.report_meter(enumerator)
+      end
+
+      def report_log(log_data_array)
+        @log_client.report_log(log_data_array)
+      end
     end
   end
 end
diff --git a/lib/skywalking/reporter/log_buffer_trigger.rb b/lib/skywalking/reporter/log_buffer_trigger.rb
new file mode 100644
index 0000000..b5de8ce
--- /dev/null
+++ b/lib/skywalking/reporter/log_buffer_trigger.rb
@@ -0,0 +1,86 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require_relative 'client/proto'
+
+module Skywalking
+  module Reporter
+    # LogBufferTrigger manages the log data queue and generates protocol messages
+    class LogBufferTrigger
+      include Enumerable
+
+      extend Forwardable
+      def_delegators :@buffer, :push
+
+      # Initialize the log buffer trigger
+      # @param config [Hash] configuration options
+      def initialize(config)
+        @config = config
+        @max_size = @config[:max_log_queue_size] || 1000
+        @buffer = Queue.new
+        @mutex = Mutex.new
+        @closed = false
+      end
+
+      # Check if the buffer is empty
+      # @return [Boolean] true if empty
+      def empty?
+        @buffer.empty?
+      end
+
+      # Add log data to the buffer
+      # @param log_data [LogData] the log data to add
+      def <<(log_data)
+        @mutex.synchronize do
+          clear_queue if @buffer.size >= @max_size
+          @buffer.push(log_data)
+        end
+      end
+
+      # Clear the queue
+      def clear_queue
+        @buffer.clear
+      end
+
+      # Close the queue
+      def close_queue
+        @mutex.synchronize do
+          @buffer.close
+          @closed = true
+        end
+      end
+
+      # Check if the queue is closed
+      # @return [Boolean] true if closed
+      def closed?
+        @closed
+      end
+
+      # Stream log data from the buffer
+      # @return [Enumerator, nil] enumerator of log data or nil if empty
+      def stream_data
+        begin
+          log_data = @buffer.pop(false)
+        rescue ThreadError
+          return nil
+        end
+
+        Enumerator.new do |yielder|
+          yielder << log_data unless log_data.nil?
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/reporter/meter_buffer_trigger.rb b/lib/skywalking/reporter/meter_buffer_trigger.rb
new file mode 100644
index 0000000..8401b5f
--- /dev/null
+++ b/lib/skywalking/reporter/meter_buffer_trigger.rb
@@ -0,0 +1,94 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+require 'forwardable'
+
+module Skywalking
+  module Reporter
+    # Buffer trigger for meter data collection
+    class MeterBufferTrigger
+      include Enumerable
+
+      extend Forwardable
+      def_delegators :@buffer, :push
+
+      # @param config [Hash] configuration
+      def initialize(config)
+        @config = config
+        @max_size = @config[:max_meter_queue_size] || 1000
+        @buffer = Queue.new
+        @mutex = Mutex.new
+        @closed = false
+      end
+
+      # Check if buffer is empty
+      # @return [Boolean]
+      def empty?
+        @buffer.empty?
+      end
+
+      # Add meter data to buffer
+      # @param meter_data [MeterData] the meter data to add
+      def <<(meter_data)
+        @mutex.synchronize do
+          clear_queue if @buffer.size >= @max_size
+          @buffer.push(meter_data)
+        end
+      end
+
+      # Clear all data from the queue
+      def clear_queue
+        @buffer.clear
+      end
+
+      # Close the queue
+      def close_queue
+        @mutex.synchronize do
+          @buffer.close
+          @closed = true
+        end
+      end
+
+      # Check if the queue is closed
+      # @return [Boolean]
+      def closed?
+        @closed
+      end
+
+      # Stream meter data from the buffer
+      # @return [Enumerator, nil]
+      def stream_data
+        data_batch = []
+
+        # Collect up to 100 items or until timeout
+        deadline = Time.now + 0.1
+        while Time.now < deadline && data_batch.size < 100
+          begin
+            meter_data = @buffer.pop(true)
+            data_batch << meter_data
+          rescue ThreadError
+            break
+          end
+        end
+
+        return nil if data_batch.empty?
+
+        Enumerator.new do |yielder|
+          data_batch.each { |data| yielder << data }
+        end
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/reporter/protocol.rb b/lib/skywalking/reporter/protocol.rb
index 2e5fb28..833bb54 100644
--- a/lib/skywalking/reporter/protocol.rb
+++ b/lib/skywalking/reporter/protocol.rb
@@ -19,7 +19,7 @@
       def report_heartbeat
         raise NotImplementedError 'The report_heartbeat method has not been implemented'
       end
-      
+
       def report_segment
         raise NotImplementedError 'The report_segment method has not been implemented'
       end
diff --git a/lib/skywalking/reporter/report.rb b/lib/skywalking/reporter/report.rb
index 6c397d5..59db2cd 100644
--- a/lib/skywalking/reporter/report.rb
+++ b/lib/skywalking/reporter/report.rb
@@ -16,12 +16,24 @@
 require_relative 'grpc'
 require_relative 'scheduler'
 require_relative 'buffer_trigger'
+require_relative 'meter_buffer_trigger'
+require_relative 'reporter_manager'
+require_relative '../meter/base'
+require_relative '../meter/meter_service'
+require_relative '../meter/runtime/cpu_data_source'
+require_relative '../meter/runtime/mem_data_source'
+require_relative '../meter/runtime/gc_data_source'
+require_relative '../meter/runtime/thread_data_source'
+require_relative '../meter/runtime/ruby_runtime_data_source'
 
 module Skywalking
   module Reporter
     class Report
       def initialize(config)
         @config = config
+        @meter_service = nil
+        @scheduler_loop = nil
+        @reporter_manager = nil
         init_proto
       end
 
@@ -37,22 +49,48 @@
       end
 
       def init_reporter
-        @daemon_loop = []
-
+        # Initialize scheduler for heartbeat
         @scheduler_loop = Scheduler.new
-        @daemon_loop << Thread.new do
+        @scheduler_thread = Thread.new do
+          Thread.current.name = 'Scheduler'
           init_worker_loop
           @scheduler_loop.run
         end
 
-        @@trigger = BufferTrigger.new(@config)
-        @daemon_loop << Thread.new do
-          report_segment
+        # Initialize reporter manager
+        @reporter_manager = ReporterManager.new(@config, @protocol)
+
+        # Register segment reporter
+        segment_trigger = BufferTrigger.new(@config)
+        @reporter_manager.register_reporter(:segment, segment_trigger, :report_segment)
+
+        # Register meter reporter if enabled
+        if @config[:meter_reporter_active]
+          meter_trigger = MeterBufferTrigger.new(@config)
+          @reporter_manager.register_reporter(:meter, meter_trigger, :report_meter)
+
+          # Initialize meter service
+          @meter_service = Skywalking::Meter::MeterService.new(@config, meter_trigger)
+
+          if @config[:runtime_meter_reporter_active]
+            register_runtime_data_sources
+          end
+
+          @meter_service.start
         end
+
+        # Start all reporters
+        @reporter_manager.start
       end
 
+      # Deprecated: Use instance method instead
       def self.trigger
-        @@trigger
+        warn "[DEPRECATED] Report.trigger is deprecated. Use instance method 'trigger' instead."
+        default_instance.trigger
+      end
+
+      def self.default_instance
+        @default_instance ||= new(Skywalking::Configuration.new)
       end
 
       def init_worker_loop
@@ -60,23 +98,42 @@
       end
 
       def stop
-        @scheduler_loop.shutdown
-        @@trigger.close_queue
-        @daemon_loop.each do |daemon|
-          if daemon.alive?
-            daemon.wakeup
-            daemon.join
-          end
-        end
+        @scheduler_loop&.shutdown
+        @scheduler_thread&.join(5)
+        @reporter_manager&.stop
+        @meter_service&.stop
       end
 
       def report_heartbeat
         @protocol.report_heartbeat
       end
 
-      def report_segment
-        @protocol.report_segment(@@trigger.stream_data) until @@trigger.closed?
+      # Accessor methods for triggers
+      def trigger
+        @reporter_manager&.trigger(:segment)
+      end
+
+      def meter_trigger
+        @reporter_manager&.trigger(:meter)
+      end
+
+      # Report log data to the backend
+      # @param log_data_array [Array<LogData>] array of log data to report
+      def report_log(log_data_array)
+        @protocol.report_log(log_data_array) if @protocol
+      rescue => e
+        warn "Failed to report log data: #{e.message}"
+      end
+
+      private
+
+      def register_runtime_data_sources
+        Skywalking::Meter::Runtime::CpuDataSource.new.register(@meter_service)
+        Skywalking::Meter::Runtime::MemDataSource.new.register(@meter_service)
+        Skywalking::Meter::Runtime::GcDataSource.new.register(@meter_service)
+        Skywalking::Meter::Runtime::ThreadDataSource.new.register(@meter_service)
+        Skywalking::Meter::Runtime::RubyRuntimeDataSource.new.register(@meter_service)
       end
     end
   end
-end
\ No newline at end of file
+end
diff --git a/lib/skywalking/reporter/reporter_manager.rb b/lib/skywalking/reporter/reporter_manager.rb
new file mode 100644
index 0000000..7be9a9f
--- /dev/null
+++ b/lib/skywalking/reporter/reporter_manager.rb
@@ -0,0 +1,92 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+module Skywalking
+  module Reporter
+    # Manages all reporters in a more elegant way using dependency injection
+    class ReporterManager
+      include Log::Logging
+
+      attr_reader :reporters, :triggers
+
+      def initialize(config, protocol)
+        @config = config
+        @protocol = protocol
+        @reporters = {}
+        @triggers = {}
+        @threads = []
+        @running = false
+      end
+
+      # Register a reporter with its trigger
+      # @param name [Symbol] reporter name
+      # @param trigger [Object] buffer trigger instance
+      # @param report_method [Symbol] method name on protocol to call
+      def register_reporter(name, trigger, report_method)
+        @triggers[name] = trigger
+        @reporters[name] = {
+          trigger: trigger,
+          report_method: report_method,
+          thread: nil
+        }
+      end
+
+      # Start all registered reporters
+      def start
+        return if @running
+        @running = true
+
+        @reporters.each do |name, reporter|
+          thread = Thread.new do
+            Thread.current.name = "Reporter-#{name}"
+            report_loop(reporter[:trigger], reporter[:report_method])
+          end
+          reporter[:thread] = thread
+          @threads << thread
+        end
+      end
+
+      # Stop all reporters
+      def stop
+        @running = false
+
+        @triggers.each_value(&:close_queue)
+        @threads.each do |thread|
+          thread.join(5) if thread.alive?
+        end
+        @threads.clear
+      end
+
+      # Get trigger by name
+      # @param name [Symbol] trigger name
+      # @return [Object, nil] trigger instance
+      def trigger(name)
+        @triggers[name]
+      end
+
+      private
+
+      def report_loop(trigger, method_name)
+        while @running && !trigger.closed?
+          data = trigger.stream_data
+          @protocol.send(method_name, data) if data
+        end
+      rescue => e
+        error "Error in reporter loop: #{e.message}"
+        error e.backtrace.join("\n")
+      end
+    end
+  end
+end
diff --git a/lib/skywalking/tracing/span_context.rb b/lib/skywalking/tracing/span_context.rb
index fa882cc..39279a6 100644
--- a/lib/skywalking/tracing/span_context.rb
+++ b/lib/skywalking/tracing/span_context.rb
@@ -147,7 +147,9 @@
         spans.delete(span)
         @n_spans -= 1
         if @n_spans.zero?
-          Reporter::Report.trigger << @segment
+          if (trigger = Agent.instance&.reporter&.trigger)
+            trigger << @segment
+          end
           return true
         end
 
diff --git a/lib/skywalking/version.rb b/lib/skywalking/version.rb
index cdaca0b..b29b4b2 100644
--- a/lib/skywalking/version.rb
+++ b/lib/skywalking/version.rb
@@ -14,5 +14,5 @@
 #  limitations under the License.
 
 module Skywalking
-  VERSION = "0.1.0".freeze
+  VERSION = "0.2.0".freeze
 end
diff --git a/skywalking.gemspec b/skywalking.gemspec
index fb0c5b7..824936e 100644
--- a/skywalking.gemspec
+++ b/skywalking.gemspec
@@ -43,6 +43,11 @@
 
   # Communication with OAP
   spec.add_dependency 'grpc', '~> 1.68.0'
+  
+  # Meter dependencies
+  spec.add_dependency 'sys-cpu', '~> 1.0'
+  spec.add_dependency 'get_process_mem', '~> 0.2'
+  spec.add_dependency 'ffi', '~> 1.17', '>= 1.17.0'
 
   # Base dev dependency
   spec.add_development_dependency 'bundler', '~> 2.0'
diff --git a/spec/scenarios/elasticsearch/docker-compose.yml b/spec/scenarios/elasticsearch/docker-compose.yml
index 880ec79..4a38f1d 100644
--- a/spec/scenarios/elasticsearch/docker-compose.yml
+++ b/spec/scenarios/elasticsearch/docker-compose.yml
@@ -61,7 +61,7 @@
     entrypoint:
       - "sh"
       - "-c"
-      - "gem install sinatra rackup puma elasticsearch && ruby /app/spec/scenarios/elasticsearch/elasticsearch.rb"
+      - "gem install sinatra rackup puma elasticsearch:8.16.1 && ruby /app/spec/scenarios/elasticsearch/elasticsearch.rb"
     depends_on:
       oap:
         condition: service_healthy