1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 
9 namespace IceInternal
10 {
11 sealed class LoggerAdminI : Ice.LoggerAdminDisp_
12 {
13     public override void
attachRemoteLogger(Ice.RemoteLoggerPrx prx, Ice.LogMessageType[] messageTypes, string[] categories, int messageMax, Ice.Current current)14     attachRemoteLogger(Ice.RemoteLoggerPrx prx, Ice.LogMessageType[] messageTypes, string[] categories,
15                        int messageMax, Ice.Current current)
16     {
17         if(prx == null)
18         {
19             return; // can't send this null RemoteLogger anything!
20         }
21 
22         Ice.RemoteLoggerPrx remoteLogger = Ice.RemoteLoggerPrxHelper.uncheckedCast(prx.ice_twoway());
23 
24         Filters filters = new Filters(messageTypes, categories);
25         LinkedList<Ice.LogMessage> initLogMessages = null;
26 
27         lock(this)
28         {
29             if(_sendLogCommunicator == null)
30             {
31                 if(_destroyed)
32                 {
33                     throw new Ice.ObjectNotExistException();
34                 }
35 
36                 _sendLogCommunicator =
37                     createSendLogCommunicator(current.adapter.getCommunicator(), _logger.getLocalLogger());
38             }
39 
40             Ice.Identity remoteLoggerId = remoteLogger.ice_getIdentity();
41 
42             if(_remoteLoggerMap.ContainsKey(remoteLoggerId))
43             {
44                 if(_traceLevel > 0)
45                 {
46                     _logger.trace(_traceCategory, "rejecting `" + remoteLogger.ToString() +
47                                  "' with RemoteLoggerAlreadyAttachedException");
48                 }
49 
50                 throw new Ice.RemoteLoggerAlreadyAttachedException();
51             }
52 
53             _remoteLoggerMap.Add(remoteLoggerId,
54                                  new RemoteLoggerData(changeCommunicator(remoteLogger, _sendLogCommunicator), filters));
55 
56             if(messageMax != 0)
57             {
58                 initLogMessages = new LinkedList<Ice.LogMessage>(_queue); // copy
59             }
60             else
61             {
62                 initLogMessages = new LinkedList<Ice.LogMessage>();
63             }
64         }
65 
66         if(_traceLevel > 0)
67         {
68             _logger.trace(_traceCategory, "attached `" + remoteLogger.ToString() + "'");
69         }
70 
71         if(initLogMessages.Count > 0)
72         {
73             filterLogMessages(initLogMessages, filters.messageTypes, filters.traceCategories, messageMax);
74         }
75 
76         try
77         {
78             remoteLogger.initAsync(_logger.getPrefix(), initLogMessages.ToArray()).ContinueWith(
79                 (t) =>
80                 {
81                     try
82                     {
83                         t.Wait();
84                         if(_traceLevel > 1)
85                         {
86                             _logger.trace(_traceCategory,"init on `" + remoteLogger.ToString()
87                                           + "' completed successfully");
88                         }
89                     }
90                     catch(System.AggregateException ae)
91                     {
92                         Debug.Assert(ae.InnerException is Ice.LocalException);
93                         deadRemoteLogger(remoteLogger, _logger, (Ice.LocalException)ae.InnerException, "init");
94                     }
95                 });
96         }
97         catch(Ice.LocalException ex)
98         {
99             deadRemoteLogger(remoteLogger, _logger, ex, "init");
100             throw;
101         }
102     }
103 
104     public override bool
detachRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Current current)105     detachRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Current current)
106     {
107         if(remoteLogger == null)
108         {
109             return false;
110         }
111 
112         //
113         // No need to convert the proxy as we only use its identity
114         //
115         bool found = removeRemoteLogger(remoteLogger);
116 
117         if(_traceLevel > 0)
118         {
119             if(found)
120             {
121                 _logger.trace(_traceCategory,  "detached `" + remoteLogger.ToString() + "'");
122             }
123             else
124             {
125                 _logger.trace(_traceCategory, "cannot detach `" + remoteLogger.ToString() + "': not found");
126             }
127         }
128 
129         return found;
130     }
131 
132     public override Ice.LogMessage[]
getLog(Ice.LogMessageType[] messageTypes, string[] categories, int messageMax, out string prefix, Ice.Current current)133     getLog(Ice.LogMessageType[] messageTypes, string[] categories, int messageMax, out string prefix,
134            Ice.Current current)
135     {
136         LinkedList<Ice.LogMessage> logMessages = null;
137         lock(this)
138         {
139             if(messageMax != 0)
140             {
141                 logMessages = new LinkedList<Ice.LogMessage>(_queue);
142             }
143             else
144             {
145                 logMessages = new LinkedList<Ice.LogMessage>();
146             }
147         }
148 
149         prefix = _logger.getPrefix();
150 
151         if(logMessages.Count > 0)
152         {
153             Filters filters = new Filters(messageTypes, categories);
154             filterLogMessages(logMessages, filters.messageTypes, filters.traceCategories, messageMax);
155         }
156         return logMessages.ToArray();
157     }
158 
LoggerAdminI(Ice.Properties props, LoggerAdminLoggerI logger)159     internal LoggerAdminI(Ice.Properties props, LoggerAdminLoggerI logger)
160     {
161         _maxLogCount = props.getPropertyAsIntWithDefault("Ice.Admin.Logger.KeepLogs", 100);
162         _maxTraceCount = props.getPropertyAsIntWithDefault("Ice.Admin.Logger.KeepTraces", 100);
163         _traceLevel = props.getPropertyAsInt("Ice.Trace.Admin.Logger");
164         _logger = logger;
165     }
166 
destroy()167     internal void destroy()
168     {
169         Ice.Communicator sendLogCommunicator = null;
170 
171         lock(this)
172         {
173             if(!_destroyed)
174             {
175                 _destroyed = true;
176                 sendLogCommunicator = _sendLogCommunicator;
177                 _sendLogCommunicator = null;
178             }
179         }
180 
181         //
182         // Destroy outside lock to avoid deadlock when there are outstanding two-way log calls sent to
183         // remote loggers
184         //
185         if(sendLogCommunicator != null)
186         {
187             sendLogCommunicator.destroy();
188         }
189     }
190 
log(Ice.LogMessage logMessage)191     internal List<Ice.RemoteLoggerPrx> log(Ice.LogMessage logMessage)
192     {
193         lock(this)
194         {
195             List<Ice.RemoteLoggerPrx> remoteLoggers = null;
196 
197             //
198             // Put message in _queue
199             //
200             if((logMessage.type != Ice.LogMessageType.TraceMessage && _maxLogCount > 0) ||
201                (logMessage.type == Ice.LogMessageType.TraceMessage && _maxTraceCount > 0))
202             {
203                 _queue.AddLast(logMessage);
204 
205                 if(logMessage.type != Ice.LogMessageType.TraceMessage)
206                 {
207                     Debug.Assert(_maxLogCount > 0);
208                     if(_logCount == _maxLogCount)
209                     {
210                         //
211                         // Need to remove the oldest log from the queue
212                         //
213                         Debug.Assert(_oldestLog != null);
214                         var next = _oldestLog.Next;
215                         _queue.Remove(_oldestLog);
216                         _oldestLog = next;
217 
218                         while(_oldestLog != null && _oldestLog.Value.type == Ice.LogMessageType.TraceMessage)
219                         {
220                             _oldestLog = _oldestLog.Next;
221                         }
222                         Debug.Assert(_oldestLog != null); // remember: we just added a Log at the end
223                     }
224                     else
225                     {
226                         Debug.Assert(_logCount < _maxLogCount);
227                         _logCount++;
228                         if(_oldestLog == null)
229                         {
230                             _oldestLog = _queue.Last;
231                         }
232                     }
233                 }
234                 else
235                 {
236                     Debug.Assert(_maxTraceCount > 0);
237                     if(_traceCount == _maxTraceCount)
238                     {
239                         //
240                         // Need to remove the oldest trace from the queue
241                         //
242                         Debug.Assert(_oldestTrace != null);
243                         var next = _oldestTrace.Next;
244                         _queue.Remove(_oldestTrace);
245                         _oldestTrace = next;
246 
247                         while(_oldestTrace != null && _oldestTrace.Value.type != Ice.LogMessageType.TraceMessage)
248                         {
249                             _oldestTrace = _oldestTrace.Next;
250                         }
251                         Debug.Assert(_oldestTrace != null);  // remember: we just added a Log at the end
252                     }
253                     else
254                     {
255                         Debug.Assert(_traceCount < _maxTraceCount);
256                         _traceCount++;
257                         if(_oldestTrace == null)
258                         {
259                             _oldestTrace = _queue.Last;
260                         }
261                     }
262                 }
263             }
264 
265             //
266             // Queue updated, now find which remote loggers want this message
267             //
268             foreach(RemoteLoggerData p in _remoteLoggerMap.Values)
269             {
270                 Filters filters = p.filters;
271 
272                 if(filters.messageTypes.Count == 0 || filters.messageTypes.Contains(logMessage.type))
273                 {
274                     if(logMessage.type != Ice.LogMessageType.TraceMessage || filters.traceCategories.Count == 0 ||
275                        filters.traceCategories.Contains(logMessage.traceCategory))
276                     {
277                         if(remoteLoggers == null)
278                         {
279                             remoteLoggers = new List<Ice.RemoteLoggerPrx>();
280                         }
281                         remoteLoggers.Add(p.remoteLogger);
282                     }
283                 }
284             }
285 
286             return remoteLoggers;
287         }
288     }
289 
deadRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Logger logger, Ice.LocalException ex, string operation)290     internal void deadRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Logger logger, Ice.LocalException ex,
291                                    string operation)
292     {
293         //
294         // No need to convert remoteLogger as we only use its identity
295         //
296         if(removeRemoteLogger(remoteLogger))
297         {
298             if(_traceLevel > 0)
299             {
300                 logger.trace(_traceCategory,  "detached `" + remoteLogger.ToString() + "' because "
301                              + operation + " raised:\n" + ex.ToString());
302             }
303         }
304     }
305 
getTraceLevel()306     internal int getTraceLevel()
307     {
308         return _traceLevel;
309     }
310 
removeRemoteLogger(Ice.RemoteLoggerPrx remoteLogger)311     private bool removeRemoteLogger(Ice.RemoteLoggerPrx remoteLogger)
312     {
313         lock(this)
314         {
315             return _remoteLoggerMap.Remove(remoteLogger.ice_getIdentity());
316         }
317     }
318 
filterLogMessages(LinkedList<Ice.LogMessage> logMessages, HashSet<Ice.LogMessageType> messageTypes, HashSet<string> traceCategories, int messageMax)319     private static void filterLogMessages(LinkedList<Ice.LogMessage> logMessages,
320                                           HashSet<Ice.LogMessageType> messageTypes,
321                                           HashSet<string> traceCategories, int messageMax)
322     {
323         Debug.Assert(logMessages.Count > 0 && messageMax != 0);
324 
325         //
326         // Filter only if one of the 3 filters is set; messageMax < 0 means "give me all"
327         // that match the other filters, if any.
328         //
329         if(messageTypes.Count > 0 || traceCategories.Count > 0 || messageMax > 0)
330         {
331             int count = 0;
332             var p = logMessages.Last;
333             while(p != null)
334             {
335                 bool keepIt = false;
336                 Ice.LogMessage msg = p.Value;
337                 if(messageTypes.Count == 0 || messageTypes.Contains(msg.type))
338                 {
339                     if(msg.type != Ice.LogMessageType.TraceMessage || traceCategories.Count == 0 ||
340                        traceCategories.Contains(msg.traceCategory))
341                     {
342                         keepIt = true;
343                     }
344                 }
345 
346                 if(keepIt)
347                 {
348                     ++count;
349                     if(messageMax > 0 && count >= messageMax)
350                     {
351                         // Remove all older messages
352                         p = p.Previous;
353                         while(p != null)
354                         {
355                             var previous = p.Previous;
356                             logMessages.Remove(p);
357                             p = previous;
358                         }
359                         break; // while
360                     }
361                     else
362                     {
363                         p = p.Previous;
364                     }
365                 }
366                 else
367                 {
368                     var previous = p.Previous;
369                     logMessages.Remove(p);
370                     p = previous;
371                 }
372             }
373         }
374         // else, don't need any filtering
375     }
376 
377     //
378     // Change this proxy's communicator, while keeping its invocation timeout
379     //
changeCommunicator(Ice.RemoteLoggerPrx prx, Ice.Communicator communicator)380     private static Ice.RemoteLoggerPrx changeCommunicator(Ice.RemoteLoggerPrx prx, Ice.Communicator communicator)
381     {
382         if(prx == null)
383         {
384             return null;
385         }
386 
387         Ice.ObjectPrx result = communicator.stringToProxy(prx.ToString());
388         return Ice.RemoteLoggerPrxHelper.uncheckedCast(result.ice_invocationTimeout(prx.ice_getInvocationTimeout()));
389     }
390 
copyProperties(string prefix, Ice.Properties from, Ice.Properties to)391     private static void copyProperties(string prefix, Ice.Properties from, Ice.Properties to)
392     {
393         foreach(var p in from.getPropertiesForPrefix(prefix))
394         {
395             to.setProperty(p.Key, p.Value);
396         }
397     }
398 
createSendLogCommunicator(Ice.Communicator communicator, Ice.Logger logger)399     private static Ice.Communicator createSendLogCommunicator(Ice.Communicator communicator, Ice.Logger logger)
400     {
401         Ice.InitializationData initData = new Ice.InitializationData();
402         initData.logger = logger;
403         initData.properties = Ice.Util.createProperties();
404 
405         Ice.Properties mainProps = communicator.getProperties();
406 
407         copyProperties("Ice.Default.Locator", mainProps, initData.properties);
408         copyProperties("Ice.Plugin.IceSSL", mainProps, initData.properties);
409         copyProperties("IceSSL.", mainProps, initData.properties);
410 
411         string[] extraProps = mainProps.getPropertyAsList("Ice.Admin.Logger.Properties");
412 
413         if(extraProps.Length > 0)
414         {
415             for(int i = 0; i < extraProps.Length; ++i)
416             {
417                 string p = extraProps[i];
418                 if(!p.StartsWith("--"))
419                 {
420                     extraProps[i] = "--" + p;
421                 }
422             }
423             initData.properties.parseCommandLineOptions("", extraProps);
424         }
425         return Ice.Util.initialize(initData);
426     }
427 
428     private readonly LinkedList<Ice.LogMessage> _queue = new LinkedList<Ice.LogMessage>();
429     private int _logCount = 0; // non-trace messages
430     private readonly int _maxLogCount;
431     private int _traceCount = 0;
432     private readonly int _maxTraceCount;
433     private readonly int _traceLevel;
434 
435     private LinkedListNode<Ice.LogMessage> _oldestTrace = null;
436     private LinkedListNode<Ice.LogMessage> _oldestLog = null;
437 
438     private class Filters
439     {
Filters(Ice.LogMessageType[] m, string[] c)440         internal Filters(Ice.LogMessageType[] m, string[] c)
441         {
442             messageTypes = new HashSet<Ice.LogMessageType>(m);
443             traceCategories = new HashSet<string>(c);
444         }
445 
446         internal readonly HashSet<Ice.LogMessageType> messageTypes;
447         internal readonly HashSet<string> traceCategories;
448     }
449 
450     private class RemoteLoggerData
451     {
RemoteLoggerData(Ice.RemoteLoggerPrx prx, Filters f)452         internal RemoteLoggerData(Ice.RemoteLoggerPrx prx, Filters f)
453         {
454             remoteLogger = prx;
455             filters = f;
456         }
457 
458         internal readonly Ice.RemoteLoggerPrx remoteLogger;
459         internal readonly Filters filters;
460     }
461 
462     private readonly Dictionary<Ice.Identity, RemoteLoggerData> _remoteLoggerMap
463         = new Dictionary<Ice.Identity, RemoteLoggerData>();
464 
465     private readonly LoggerAdminLoggerI _logger;
466 
467     private Ice.Communicator _sendLogCommunicator = null;
468     private bool _destroyed = false;
469     static private readonly string _traceCategory = "Admin.Logger";
470 }
471 
472 }
473