public
Rubygem
Description: Merb Core: All you need. None you don't.
Homepage: http://www.merbivore.com
Clone URL: git://github.com/wycats/merb-core.git
Attempt threadsafe logger
wycats (author)
Fri Sep 26 22:12:14 -0700 2008
commit  d22b8e6c60b48718e7484a1154a2211c5fa17404
tree    9d93c09c0802c69379c67e15cd863be22b5fa372
parent  205f2436a741841abc92901c015f3c470a5b99b7
...
94
95
96
97
 
 
98
99
100
101
102
 
 
103
104
105
...
265
266
267
268
 
 
 
 
 
 
 
 
 
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
286
287
...
94
95
96
 
97
98
99
100
101
102
103
104
105
106
107
108
...
268
269
270
 
271
272
273
274
275
276
277
278
279
280
281
282
283
284
 
 
 
 
 
 
 
 
 
 
 
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
0
@@ -94,12 +94,15 @@ module Merb
0
     # argv<String, Hash>::
0
     #   The config arguments to start Merb with. Defaults to +ARGV+.
0
     def start(argv=ARGV)
0
-      Merb.logger = Merb::Logger.new(STDOUT, :warn)
0
+      # Hardcode the log stream to STDOUT
0
+      Merb::Config[:log_stream] = STDOUT
0
       if Hash === argv
0
         Merb::Config.setup(argv)
0
       else
0
         Merb::Config.parse_args(argv)
0
       end
0
+      Merb::Config[:log_stream] = STDOUT
0
+      
0
       Merb.environment = Merb::Config[:environment]
0
       Merb.root = Merb::Config[:merb_root]
0
       case Merb::Config[:action]
0
@@ -265,23 +268,43 @@ module Merb
0
     end
0
 
0
     # Logger settings
0
-    attr_accessor :logger
0
+    def logger
0
+      Thread.current[:merb_logger] ||= Merb::Logger.new
0
+    end
0
+
0
+    def logger=(obj)
0
+      unless obj
0
+        Thread.current[:merb_logger] = nil
0
+      end
0
+    end
0
 
0
     # ==== Returns
0
     # String::
0
     #   The path to the log file. If this Merb instance is running as a daemon
0
     #   this will return +STDOUT+.
0
-    def log_file(port = nil)
0
-      # if Merb::Config[:log_file]
0
-      #   Merb::Config[:log_file]
0
-      if Merb.testing?
0
-        log_path / "merb_test.log"
0
-      elsif !Merb::Config[:daemonize]
0
-        STDOUT
0
-      elsif port
0
-        log_path / "merb.#{port}.log"
0
-      else
0
-        log_path / "merb.main.log"
0
+    def log_stream(port = "main")
0
+      @streams ||= {}
0
+      @streams[port] ||= begin
0
+        log = if Merb.testing?
0
+          log_path / "merb_test.log"
0
+        elsif !Merb::Config[:daemonize]
0
+          STDOUT
0
+        else
0
+          log_path / "merb.#{port}.log"
0
+        end        
0
+        
0
+        if log.is_a?(IO)
0
+          stream = log
0
+        elsif File.exist?(log)
0
+          stream = File.open(log, (File::WRONLY | File::APPEND))
0
+        else
0
+          FileUtils.mkdir_p(File.dirname(log))
0
+          stream = File.open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
+          stream.write("#{Time.now.httpdate} #{delimiter} " \
0
+            "info #{delimiter} Logfile created\n")
0
+        end
0
+        stream.sync = true
0
+        stream
0
       end
0
     end
0
 
...
145
146
147
148
149
 
 
 
 
 
 
 
 
 
 
150
151
152
...
280
281
282
283
284
 
 
 
285
286
287
...
145
146
147
 
 
148
149
150
151
152
153
154
155
156
157
158
159
160
...
288
289
290
 
 
291
292
293
294
295
296
0
@@ -145,8 +145,16 @@ class Merb::BootLoader::Logger < Merb::BootLoader
0
 
0
   # Sets Merb.logger to a new logger created based on the config settings.
0
   def self.run
0
-    Merb.logger = Merb::Logger.new(Merb.log_file, Merb::Config[:log_level], 
0
-      Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush])
0
+    Merb::Config[:log_level] ||= begin
0
+      if Merb.environment == "production"
0
+        Merb::Logger::Levels[:warn]
0
+      else
0
+        Merb::Logger::Levels[:debug]
0
+      end          
0
+    end
0
+    
0
+    Merb::Config[:log_stream] = Merb.log_stream
0
+    
0
     print_warnings
0
   end
0
   
0
@@ -280,8 +288,9 @@ class Merb::BootLoader::Dependencies < Merb::BootLoader
0
   end
0
 
0
   def self.update_logger
0
-    updated_logger_options = [ Merb.log_file, Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush] ]
0
-    Merb::BootLoader::Logger.run if updated_logger_options != Merb.logger.init_args
0
+    # Clear out the logger so that any changes in init.rb will be
0
+    # picked up
0
+    Merb.logger = nil
0
   end
0
 
0
   private
...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
11
12
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
0
@@ -11,204 +11,205 @@ class Merb::Logger < Extlib::Logger
0
 end
0
 
0
 # require "time" # httpdate
0
-# # ==== Public Merb Logger API
0
-# #
0
-# # To replace an existing logger with a new one:
0
-# #  Merb::Logger.set_log(log{String, IO},level{Symbol, String})
0
-# #
0
-# # Available logging levels are
0
-# #   Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
0
-# #
0
-# # Logging via:
0
-# #   Merb.logger.fatal(message<String>,&block)
0
-# #   Merb.logger.error(message<String>,&block)
0
-# #   Merb.logger.warn(message<String>,&block)
0
-# #   Merb.logger.info(message<String>,&block)
0
-# #   Merb.logger.debug(message<String>,&block)
0
-# #
0
-# # Logging with autoflush:
0
-# #   Merb.logger.fatal!(message<String>,&block)
0
-# #   Merb.logger.error!(message<String>,&block)
0
-# #   Merb.logger.warn!(message<String>,&block)
0
-# #   Merb.logger.info!(message<String>,&block)
0
-# #   Merb.logger.debug!(message<String>,&block)
0
-# #
0
-# # Flush the buffer to 
0
-# #   Merb.logger.flush
0
-# #
0
-# # Remove the current log object
0
-# #   Merb.logger.close
0
-# # 
0
-# # ==== Private Merb Logger API
0
-# # 
0
-# # To initialize the logger you create a new object, proxies to set_log.
0
-# #   Merb::Logger.new(log{String, IO},level{Symbol, String})
0
-# module Merb
0
-# 
0
-#   class << self
0
-#     attr_accessor :logger
0
-#   end
0
-# 
0
-#   class Logger
0
-# 
0
-#     attr_accessor :level
0
-#     attr_accessor :delimiter
0
-#     attr_accessor :auto_flush
0
-#     attr_reader   :buffer
0
-#     attr_reader   :log
0
-#     attr_reader   :init_args
0
-# 
0
-#     # ==== Notes
0
-#     # Ruby (standard) logger levels:
0
-#     # :fatal:: An unhandleable error that results in a program crash
0
-#     # :error:: A handleable error condition
0
-#     # :warn:: A warning
0
-#     # :info:: generic (useful) information about system operation
0
-#     # :debug:: low-level information for developers
0
-#     Levels = 
0
-#     {
0
-#       :fatal => 7, 
0
-#       :error => 6, 
0
-#       :warn  => 4,
0
-#       :info  => 3,
0
-#       :debug => 0
0
-#     } unless const_defined?(:Levels)
0
-# 
0
-#     private
0
-# 
0
-#     # Readies a log for writing.
0
-#     #
0
-#     # ==== Parameters
0
-#     # log<IO, String>:: Either an IO object or a name of a logfile.
0
-#     def initialize_log(log)
0
-#       close if @log # be sure that we don't leave open files laying around.
0
-# 
0
-#       if log.respond_to?(:write)
0
-#         @log = log
0
-#       elsif File.exist?(log)
0
-#         @log = open(log, (File::WRONLY | File::APPEND))
0
-#         @log.sync = true
0
-#       else
0
-#         FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
0
-#         @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
-#         @log.sync = true
0
-#         @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
0
-#       end
0
-#     end
0
-# 
0
-#     public
0
-# 
0
-#     # To initialize the logger you create a new object, proxies to set_log.
0
-#     #
0
-#     # ==== Parameters
0
-#     # *args:: Arguments to create the log from. See set_logs for specifics.
0
-#     def initialize(*args)
0
-#       @init_args = args
0
-#       set_log(*args)
0
-#     end
0
-# 
0
-#     # Replaces an existing logger with a new one.
0
-#     # 
0
-#     # ==== Parameters
0
-#     # log<IO, String>:: Either an IO object or a name of a logfile.
0
-#     # log_level<~to_sym>::
0
-#     #   The log level from, e.g. :fatal or :info. Defaults to :error in the
0
-#     #   production environment and :debug otherwise.
0
-#     # delimiter<String>::
0
-#     #   Delimiter to use between message sections. Defaults to " ~ ".
0
-#     # auto_flush<Boolean>::
0
-#     #   Whether the log should automatically flush after new messages are
0
-#     #   added. Defaults to false.
0
-#     def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
0
-#       if log_level && Levels[log_level.to_sym]
0
-#         @level = Levels[log_level.to_sym]
0
-#       elsif Merb.environment == "production"
0
-#         @level = Levels[:warn]
0
-#       else
0
-#         @level = Levels[:debug]
0
-#       end
0
-#       @buffer     = []
0
-#       @delimiter  = delimiter
0
-#       @auto_flush = auto_flush
0
-# 
0
-#       initialize_log(log)
0
-# 
0
-#       Merb.logger = self
0
-#     end
0
-# 
0
-#     # Flush the entire buffer to the log object.
0
-#     def flush
0
-#       return unless @buffer.size > 0
0
-#       @log.write(@buffer.slice!(0..-1).to_s)
0
-#     end
0
-# 
0
-#     # Close and remove the current log object.
0
-#     def close
0
-#       flush
0
-#       @log.close if @log.respond_to?(:close) && !@log.tty?
0
-#       @log = nil
0
-#     end
0
-# 
0
-#     # Appends a message to the log. The methods yield to an optional block and
0
-#     # the output of this block will be appended to the message.
0
-#     #
0
-#     # ==== Parameters
0
-#     # string<String>:: The message to be logged. Defaults to nil.
0
-#     #
0
-#     # ==== Returns
0
-#     # String:: The resulting message added to the log file.
0
-#     def <<(string = nil)
0
-#       message = ""
0
-#       message << delimiter
0
-#       message << string if string
0
-#       message << "\n" unless message[-1] == ?\n
0
-#       @buffer << message
0
-#       flush if @auto_flush
0
-# 
0
-#       message
0
-#     end
0
-#     alias :push :<<
0
-# 
0
-#     # Generate the logging methods for Merb.logger for each log level.
0
-#     Levels.each_pair do |name, number|
0
-#       class_eval <<-LEVELMETHODS, __FILE__, __LINE__
0
-# 
0
-#       # Appends a message to the log if the log level is at least as high as
0
-#       # the log level of the logger.
0
-#       #
0
-#       # ==== Parameters
0
-#       # string<String>:: The message to be logged. Defaults to nil.
0
-#       #
0
-#       # ==== Returns
0
-#       # self:: The logger object for chaining.
0
-#       def #{name}(message = nil)
0
-#         self << message if #{number} >= level
0
-#         self
0
-#       end
0
-# 
0
-#       # Appends a message to the log if the log level is at least as high as
0
-#       # the log level of the logger. The bang! version of the method also auto
0
-#       # flushes the log buffer to disk.
0
-#       #
0
-#       # ==== Parameters
0
-#       # string<String>:: The message to be logged. Defaults to nil.
0
-#       #
0
-#       # ==== Returns
0
-#       # self:: The logger object for chaining.
0
-#       def #{name}!(message = nil)
0
-#         self << message if #{number} >= level
0
-#         flush if #{number} >= level
0
-#         self
0
-#       end
0
-# 
0
-#       # ==== Returns
0
-#       # Boolean:: True if this level will be logged by this logger.
0
-#       def #{name}?
0
-#         #{number} >= level
0
-#       end
0
-#       LEVELMETHODS
0
-#     end
0
-# 
0
-#   end
0
-#   
0
-# end
0
+# ==== Public Merb Logger API
0
+#
0
+# To replace an existing logger with a new one:
0
+#  Merb::Logger.set_log(log{String, IO},level{Symbol, String})
0
+#
0
+# Available logging levels are
0
+#   Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
0
+#
0
+# Logging via:
0
+#   Merb.logger.fatal(message<String>,&block)
0
+#   Merb.logger.error(message<String>,&block)
0
+#   Merb.logger.warn(message<String>,&block)
0
+#   Merb.logger.info(message<String>,&block)
0
+#   Merb.logger.debug(message<String>,&block)
0
+#
0
+# Logging with autoflush:
0
+#   Merb.logger.fatal!(message<String>,&block)
0
+#   Merb.logger.error!(message<String>,&block)
0
+#   Merb.logger.warn!(message<String>,&block)
0
+#   Merb.logger.info!(message<String>,&block)
0
+#   Merb.logger.debug!(message<String>,&block)
0
+#
0
+# Flush the buffer to 
0
+#   Merb.logger.flush
0
+#
0
+# Remove the current log object
0
+#   Merb.logger.close
0
+# 
0
+# ==== Private Merb Logger API
0
+# 
0
+# To initialize the logger you create a new object, proxies to set_log.
0
+#   Merb::Logger.new(log{String, IO},level{Symbol, String})
0
+module Merb
0
+
0
+  class Logger
0
+
0
+    attr_accessor :level
0
+    attr_accessor :delimiter
0
+    attr_accessor :auto_flush
0
+    attr_reader   :buffer
0
+    attr_reader   :log
0
+    attr_reader   :init_args
0
+
0
+    # ==== Notes
0
+    # Ruby (standard) logger levels:
0
+    # :fatal:: An unhandleable error that results in a program crash
0
+    # :error:: A handleable error condition
0
+    # :warn:: A warning
0
+    # :info:: generic (useful) information about system operation
0
+    # :debug:: low-level information for developers
0
+    Levels = 
0
+    {
0
+      :fatal => 7, 
0
+      :error => 6, 
0
+      :warn  => 4,
0
+      :info  => 3,
0
+      :debug => 0
0
+    } unless const_defined?(:Levels)
0
+
0
+    @@mutex = {}
0
+
0
+    private
0
+
0
+    # Readies a log for writing.
0
+    #
0
+    # ==== Parameters
0
+    # log<IO, String>:: Either an IO object or a name of a logfile.
0
+    def initialize_log(log)
0
+      close if @log # be sure that we don't leave open files laying around.
0
+
0
+      if log.respond_to?(:write)
0
+        @log = log
0
+      elsif File.exist?(log)
0
+        @log = open(log, (File::WRONLY | File::APPEND))
0
+        @log.sync = true
0
+      else
0
+        FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
0
+        @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
+        @log.sync = true
0
+        @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
0
+      end
0
+    end
0
+
0
+    public
0
+
0
+    # To initialize the logger you create a new object, proxies to set_log.
0
+    #
0
+    # ==== Parameters
0
+    # *args:: Arguments to create the log from. See set_logs for specifics.
0
+    def initialize(*args)
0
+      set_log(*args)
0
+    end
0
+
0
+    # Replaces an existing logger with a new one.
0
+    # 
0
+    # ==== Parameters
0
+    # log<IO, String>:: Either an IO object or a name of a logfile.
0
+    # log_level<~to_sym>::
0
+    #   The log level from, e.g. :fatal or :info. Defaults to :error in the
0
+    #   production environment and :debug otherwise.
0
+    # delimiter<String>::
0
+    #   Delimiter to use between message sections. Defaults to " ~ ".
0
+    # auto_flush<Boolean>::
0
+    #   Whether the log should automatically flush after new messages are
0
+    #   added. Defaults to false.
0
+    def set_log(stream = Merb::Config[:log_stream],
0
+      log_level = Merb::Config[:log_level], 
0
+      delimiter = Merb::Config[:log_delimiter], 
0
+      auto_flush = Merb::Config[:log_auto_flush])
0
+      
0
+      @buffer                   = []
0
+      @delimiter                = delimiter
0
+      @auto_flush               = auto_flush
0
+      
0
+      if Levels[log_level]
0
+        @level                  = Levels[log_level]
0
+      else
0
+        @level                  = log_level
0
+      end
0
+
0
+      @log                      = stream
0
+      @mutex = (@@mutex[@log] ||= Mutex.new)
0
+    end
0
+
0
+    # Flush the entire buffer to the log object.
0
+    def flush
0
+      return unless @buffer.size > 0
0
+      @mutex.synchronize do
0
+        @log.write(@buffer.slice!(0..-1).to_s)
0
+      end
0
+    end
0
+
0
+    # Close and remove the current log object.
0
+    def close
0
+      flush
0
+      @log.close if @log.respond_to?(:close) && !@log.tty?
0
+      @log = nil
0
+    end
0
+
0
+    # Appends a message to the log. The methods yield to an optional block and
0
+    # the output of this block will be appended to the message.
0
+    #
0
+    # ==== Parameters
0
+    # string<String>:: The message to be logged. Defaults to nil.
0
+    #
0
+    # ==== Returns
0
+    # String:: The resulting message added to the log file.
0
+    def <<(string = nil)
0
+      message = ""
0
+      message << delimiter
0
+      message << string if string
0
+      message << "\n" unless message[-1] == ?\n
0
+      @buffer << message
0
+      flush if @auto_flush
0
+
0
+      message
0
+    end
0
+    alias :push :<<
0
+
0
+    # Generate the logging methods for Merb.logger for each log level.
0
+    Levels.each_pair do |name, number|
0
+      class_eval <<-LEVELMETHODS, __FILE__, __LINE__
0
+
0
+      # Appends a message to the log if the log level is at least as high as
0
+      # the log level of the logger.
0
+      #
0
+      # ==== Parameters
0
+      # string<String>:: The message to be logged. Defaults to nil.
0
+      #
0
+      # ==== Returns
0
+      # self:: The logger object for chaining.
0
+      def #{name}(message = nil)
0
+        self << message if #{number} >= level
0
+        self
0
+      end
0
+
0
+      # Appends a message to the log if the log level is at least as high as
0
+      # the log level of the logger. The bang! version of the method also auto
0
+      # flushes the log buffer to disk.
0
+      #
0
+      # ==== Parameters
0
+      # string<String>:: The message to be logged. Defaults to nil.
0
+      #
0
+      # ==== Returns
0
+      # self:: The logger object for chaining.
0
+      def #{name}!(message = nil)
0
+        self << message if #{number} >= level
0
+        flush if #{number} >= level
0
+        self
0
+      end
0
+
0
+      # ==== Returns
0
+      # Boolean:: True if this level will be logged by this logger.
0
+      def #{name}?
0
+        #{number} >= level
0
+      end
0
+      LEVELMETHODS
0
+    end
0
+
0
+  end
0
+  
0
+end
...
128
129
130
131
 
132
133
134
135
 
136
137
138
...
128
129
130
 
131
132
 
 
 
133
134
135
136
0
@@ -128,11 +128,9 @@ module Merb
0
         
0
         # Store the PID for this worker
0
         Merb::Server.store_pid(port)
0
-        
0
+
0
         # Set up the logger for this worker to point to its process.
0
-        Merb.logger = Merb::Logger.new(Merb.log_file(port), 
0
-          Merb::Config[:log_level], Merb::Config[:log_delimiter], 
0
-          Merb::Config[:log_auto_flush])
0
+        Merb::Config[:log_stream] = Merb.log_stream(port)
0
           
0
         Merb.logger.warn!("Starting #{self.name.split("::").last} at port #{port}")
0
         

Comments