Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Reporter.cs « UsageReporter « Library « Duplicati - github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e89a154ed1d03886631e74ad9d2b32c129035231 (plain)
1
2
3
4
5
6
7
8
9
10
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//  Copyright (C) 2015, The Duplicati Team
//  http://www.duplicati.com, info@duplicati.com
//
//  This library is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation; either version 2.1 of the
//  License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Threading.Tasks;
using CoCoL;

namespace Duplicati.Library.UsageReporter
{
    /// <summary>
    /// The usage reporter library interface
    /// </summary>
    public static class Reporter
    {
        /// <summary>
        /// The primary input channel for new report messages
        /// </summary>
        private static IWriteChannel<ReportItem> _eventChannel;

        /// <summary>
        /// The task to await before shutdown
        /// </summary>
        private static Task ShutdownTask;

        /// <summary>
        /// Reports an event, information by default
        /// </summary>
        /// <param name="key">The event name</param>
        /// <param name="data">The event data</param>
        /// <param name="type">The event type</param>
        public static void Report(string key, string data = null, ReportType type = ReportType.Information)
        {
            if (_eventChannel != null && type >= MaxReportLevel)
                try { _eventChannel.WriteNoWait(new ReportItem(type, null, key, data)); }
                catch { }
        }

        /// <summary>
        /// Reports an event, information by default
        /// </summary>
        /// <param name="key">The event name</param>
        /// <param name="count">The event count</param>
        /// <param name="type">The event type</param>
        public static void Report(string key, long count, ReportType type = ReportType.Information)
        {
            if (_eventChannel != null && type >= MaxReportLevel)
                try { _eventChannel.WriteNoWait(new ReportItem(type, count, key, count.ToString())); }
                catch { }
        }

        /// <summary>
        /// Reports an exception event, error by default
        /// </summary>
        /// <param name="ex">The exception</param>
        /// <param name="type">The event type</param>
        public static void Report(Exception ex, ReportType type = ReportType.Warning)
        {
            if (_eventChannel != null && type >= MaxReportLevel)
                try { _eventChannel.WriteNoWait(new ReportItem(type, null, "EXCEPTION", ex.ToString())); }
                catch { }
        }

        /// <summary>
        /// Initializes the usage reporter library
        /// </summary>
        public static void Initialize()
        {
            if (_eventChannel == null || _eventChannel.IsRetiredAsync.Result)
            {
                if (IsDisabled)
                    return;

                var rsu = ReportSetUploader.Run();
                var ep = EventProcessor.Run(rsu.Item2);
                _eventChannel = ep.Item2;

                ShutdownTask = Task.WhenAll(ep.Item1, rsu.Item1);

                // TODO: Disable on debug builds
                AppDomain.CurrentDomain.UnhandledException += HandleUncaughtException;
                //AppDomain.CurrentDomain.UnhandledException += HandleUncaughtException;
                //AppDomain.CurrentDomain.ProcessExit

                Report("Started");
            }
        }


        /// <summary>
        /// Handles an uncaught exception.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="args">Arguments.</param>
        private static void HandleUncaughtException(object sender, UnhandledExceptionEventArgs args)
        {
            if (args.ExceptionObject is Exception)
                Report(args.ExceptionObject as Exception, ReportType.Crash);
        }

        /// <summary>
        /// Terminates the usage reporter library
        /// </summary>
        public static void ShutDown()
        {
            if (_eventChannel != null && !_eventChannel.IsRetiredAsync.Result)
                _eventChannel.Retire();

            if (ShutdownTask != null)
            {
                ShutdownTask.Wait(TimeSpan.FromSeconds(30));
                if (!ShutdownTask.IsCompleted)
                    Logging.Log.WriteMessage("Failed to shut down usage reporter after 30 seconds, leaving hanging ...", Logging.LogMessageType.Warning);
            }

            AppDomain.CurrentDomain.UnhandledException -= HandleUncaughtException;

        }

        /// <summary>
        /// Allow opt-out
        /// </summary>
        internal const string DISABLED_ENVNAME_TEMPLATE = "USAGEREPORTER_{0}_LEVEL";

        /// <summary>
        /// Cached value with the max report level
        /// </summary>
        private static ReportType? Cached_MaxReportLevel;

        /// <summary>
        /// A value indicating if the usage reporter library is forced disabled
        /// </summary>
        private static bool Forced_Disabled = false;

        /// <summary>
        /// Gets the environment default report level
        /// </summary>
        /// <value>The system default report level.</value>
        public static string DefaultReportLevel
        {
            get
            {
                return IsDisabledByEnvironment ? "Disabled" : MaxReportLevel.ToString();
            }
        }

        /// <summary>
        /// The maxmimum allowed report level
        /// </summary>
        /// <value>The type of the max report.</value>
        private static ReportType MaxReportLevel
        {
            get
            {
                if (Cached_MaxReportLevel == null)
                {
                    var str = Environment.GetEnvironmentVariable(string.Format(DISABLED_ENVNAME_TEMPLATE, AutoUpdater.AutoUpdateSettings.AppName));
                    ReportType tmp;
                    if (string.IsNullOrWhiteSpace(str) || !Enum.TryParse(str, out tmp))
                        Cached_MaxReportLevel = ReportType.Information;
                    else
                        Cached_MaxReportLevel = tmp;
                }

                return Cached_MaxReportLevel.Value;
            }
        }

        /// <summary>
        /// Gets a value indicating if the user has opted out of usage reporting
        /// </summary>
        /// <value><c>true</c> if is disabled; otherwise, <c>false</c>.</value>
        private static bool IsDisabled
        {
            get 
            {
                if (Forced_Disabled)
                    return true;

                return IsDisabledByEnvironment;
            }
        }

        /// <summary>
        /// Gets a value indicating if the user has opted out of usage reporting,
        /// but without reading the local override option
        /// </summary>
        /// <value><c>true</c> if is disabled; otherwise, <c>false</c>.</value>
        private static bool IsDisabledByEnvironment
        {
            get
            {
                var str = Environment.GetEnvironmentVariable(string.Format(DISABLED_ENVNAME_TEMPLATE, AutoUpdater.AutoUpdateSettings.AppName));
#if DEBUG
                // Default to not report crashes etc from debug builds
                if (string.IsNullOrWhiteSpace(str))
                    str = "none";
#endif
                return string.Equals(str, "none", StringComparison.OrdinalIgnoreCase) || Utility.Utility.ParseBool(str, false);
            }
        }

        /// <summary>
        /// Sets the usage reporter level
        /// </summary>
        /// <param name="maxreportlevel">The maximum level of events to report, or null to set default.</param>
        /// <param name="disable"><c>True</c> to disable usage reporting<c>false</c> otherwise.</param>
        public static void SetReportLevel(ReportType? maxreportlevel, bool disable)
        {
            if (disable)
            {
                Forced_Disabled = true;
                ShutDown();
            }
            else
            {
                Forced_Disabled = false;
                Cached_MaxReportLevel = maxreportlevel;
                Initialize();
            }
        }
    }
}