~ubuntu-branches/ubuntu/saucy/monodevelop/saucy-proposed

« back to all changes in this revision

Viewing changes to src/core/MonoDevelop.Core/MonoDevelop.Core.LogReporting/LogReportingService.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields, 1840cc1
  • Date: 2012-02-05 10:49:36 UTC
  • mfrom: (10.2.12)
  • Revision ID: package-import@ubuntu.com-20120205104936-f3dutq6lnseokb6d
Tags: 2.8.6.3+dfsg-1
[1840cc1] Imported Upstream version 2.8.6.3+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// LogReportingService.cs
 
3
//  
 
4
// Author:
 
5
//       Alan McGovern <alan@xamarin.com>
 
6
// 
 
7
// Copyright (c) 2011 Alan McGovern
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
using System;
 
27
using System.IO;
 
28
using System.Threading;
 
29
using System.Net;
 
30
using System.Web;
 
31
using System.IO.Compression;
 
32
 
 
33
namespace MonoDevelop.Core.LogReporting
 
34
{
 
35
        public static class LogReportingService
 
36
        {
 
37
                const string ServiceVersion = "1";
 
38
                public static readonly FilePath CrashLogDirectory = UserProfile.Current.LogDir.Combine ("LogAgent");
 
39
                
 
40
                const string ReportCrashesKey = "MonoDevelop.LogAgent.ReportCrashes";
 
41
                const string ReportUsageKey = "MonoDevelop.LogAgent.ReportUsage";
 
42
                
 
43
                static int CrashId;
 
44
                static int Processing;
 
45
                
 
46
                // Return value is the new value for 'ReportCrashes'
 
47
                // First parameter is the current value of 'ReportCrashes
 
48
                // Second parameter is the exception
 
49
                // Thirdparameter shows if the exception is fatal or not
 
50
                public static Func<bool?, Exception, bool, bool?> UnhandledErrorOccured;
 
51
                
 
52
                public static bool? ReportCrashes {
 
53
                        get { return PropertyService.Get<bool?> (ReportCrashesKey); }
 
54
                        set { PropertyService.Set (ReportCrashesKey, value); }
 
55
                }
 
56
                        
 
57
                public static bool? ReportUsage {
 
58
                        get { return PropertyService.Get<bool?> (ReportUsageKey); }
 
59
                        set { PropertyService.Set (ReportUsageKey, value); }
 
60
                }
 
61
                
 
62
                public static void ReportUnhandledException (Exception ex, bool willShutDown)
 
63
                {
 
64
                        var oldReportCrashes = ReportCrashes;
 
65
                        
 
66
                        if (UnhandledErrorOccured != null)
 
67
                                ReportCrashes = UnhandledErrorOccured (ReportCrashes, ex, willShutDown);
 
68
                        
 
69
                        // If crash reporting has been explicitly disabled, disregard this crash
 
70
                        if (ReportCrashes.HasValue && !ReportCrashes.Value)
 
71
                                return;
 
72
                        
 
73
                        byte[] data;
 
74
                        using (var stream = new MemoryStream ()) {
 
75
                                using (var writer = System.Xml.XmlWriter.Create (stream)) {
 
76
                                                writer.WriteStartElement ("CrashLog");
 
77
                                                writer.WriteAttributeString ("version", ServiceVersion);
 
78
                                                
 
79
                                                writer.WriteElementString ("SystemInformation", SystemInformation.ToText ());
 
80
                                                writer.WriteElementString ("Exception", ex.ToString ());
 
81
                                                
 
82
                                                writer.WriteEndElement ();
 
83
                                        }
 
84
                                data = stream.ToArray ();
 
85
                        }
 
86
                        
 
87
                        // Log to disk only if uploading fails.
 
88
                        var filename = string.Format ("{0}.{1}.crashlog", SystemInformation.SessionUuid, Interlocked.Increment (ref CrashId));
 
89
                        ThreadPool.QueueUserWorkItem (delegate {
 
90
                                if (!TryUploadReport (filename, data)) {
 
91
                                        if (!Directory.Exists (CrashLogDirectory))
 
92
                                                Directory.CreateDirectory (CrashLogDirectory);
 
93
 
 
94
                                        File.WriteAllBytes (CrashLogDirectory.Combine (filename), data);
 
95
                                }
 
96
                        });
 
97
                        
 
98
                        //ensure we don't lose the setting
 
99
                        if (ReportCrashes != oldReportCrashes) {
 
100
                                PropertyService.SaveProperties ();
 
101
                        }
 
102
                }
 
103
                
 
104
                public static void ProcessCache ()
 
105
                {
 
106
                        int origValue = -1;
 
107
                        try {
 
108
                                // Ensure only 1 thread at a time attempts to upload cached reports
 
109
                                origValue = Interlocked.CompareExchange (ref Processing, 1, 0);
 
110
                                if (origValue != 0)
 
111
                                        return;
 
112
                                
 
113
                                // Uploading is not enabled, so bail out
 
114
                                if (!ReportCrashes.GetValueOrDefault ())
 
115
                                        return;
 
116
                                
 
117
                                // Definitely no crash reports if this doesn't exist
 
118
                                if (!Directory.Exists (CrashLogDirectory))
 
119
                                        return;
 
120
                                
 
121
                                foreach (var file in Directory.GetFiles (CrashLogDirectory)) {
 
122
                                        if (TryUploadReport (file, File.ReadAllBytes (file)))
 
123
                                                File.Delete (file);
 
124
                                }
 
125
                        } catch (Exception ex) {
 
126
                                LoggingService.LogError ("Exception processing cached crashes", ex);
 
127
                        } finally {
 
128
                                if (origValue == 0)
 
129
                                        Interlocked.CompareExchange (ref Processing, 0, 1);
 
130
                        }
 
131
                }
 
132
 
 
133
                static bool TryUploadReport (string filename, byte[] data)
 
134
                {
 
135
                        try {
 
136
                                var server = Environment.GetEnvironmentVariable ("MONODEVELOP_CRASHREPORT_SERVER");
 
137
                                if (string.IsNullOrEmpty (server))
 
138
                                        server = "monodevlog.xamarin.com:35162";
 
139
 
 
140
                                var request = (HttpWebRequest) WebRequest.Create (string.Format ("http://{0}/logagentreport/", server));
 
141
                                request.Headers.Add ("LogAgentVersion", ServiceVersion);
 
142
                                request.Headers.Add ("LogAgent_Filename", Path.GetFileName (filename));
 
143
                                request.Headers.Add ("Content-Encoding", "gzip");
 
144
                                request.Method = "POST";
 
145
                                
 
146
                                // Compress the data and then use the compressed length in ContentLength
 
147
                                var compressed = new MemoryStream ();
 
148
                                using (var zipper = new GZipStream (compressed, CompressionMode.Compress))
 
149
                                        zipper.Write (data, 0, data.Length);
 
150
                                data = compressed.ToArray ();
 
151
                                
 
152
                                request.ContentLength = data.Length;
 
153
                                using (var requestStream = request.GetRequestStream ())
 
154
                                        requestStream.Write (data, 0, data.Length);
 
155
                                
 
156
                                LoggingService.LogDebug ("CrashReport sent to server, awaiting response...");
 
157
 
 
158
                                // Ensure the server has correctly processed everything.
 
159
                                using (var response = (HttpWebResponse) request.GetResponse ()) {
 
160
                                        if (response.StatusCode != HttpStatusCode.OK) {
 
161
                                                LoggingService.LogError ("Server responded with status code {1} and error: {0}", response.StatusDescription, response.StatusCode);
 
162
                                                return false;
 
163
                                        }
 
164
                                }
 
165
                        } catch (Exception ex) {
 
166
                                LoggingService.LogError ("Failed to upload report to the server", ex);
 
167
                                return false;
 
168
                        }
 
169
 
 
170
                        LoggingService.LogDebug ("Successfully uploaded crash report");
 
171
                        return true;
 
172
                }
 
173
        }
 
174
}
 
175