-
Notifications
You must be signed in to change notification settings - Fork 7
/
LoggerService.java
250 lines (223 loc) · 8.5 KB
/
LoggerService.java
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package im.kola.Yalo.log;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* Created by zivsegal on 4/3/14.
*/
public class LoggerService extends IntentService{
private static String TAG = "LoggerService";
public enum LogMod {
silent,
active
}
private static LogMod mode = LogMod.silent;
public static final String ACTION_LOG = "LOGGER_SERVICE_ACTION_LOG";
public static final String ACTION_SET_MODE = "LOGGER_SERVICE_ACTION_SET_MODE";
public static final String ACTION_CLEANUP = "LOGGER_SERVICE_ACTION_CLEANUP"; // Set by an alarm for daily old log files cleanup
public static final String EXTRA_LOG = "EXTRA_LOG";
public static final String EXTRA_MODE = "EXTRA_MODE";
private static final String LOGSTASH_SERVER_URL = "http://logger.yaloapp.com/";
private static final int LOGSTASH_UDP_JSON_PORT = 5958;
private static final String LOGSTASH_FILE_PREFIX= "logstash_";
private static final int MAX_LOG_DAYS = 7;
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
private static final int DAY = 24*60*60*1000; // in milliseconds
public LoggerService() {
super("LoggerService");
}
@Override
public void onCreate() {
super.onCreate();
setCleanupWakeAlarm(DAY);
}
/**
* Start this service to perform a writing action with the given parameters. If
* the service is already performing a task this action will be queued.*
*
* @param context
* @param log the log row to be written
*/
public static void writeToFile(Context context, String log) {
Intent intent = new Intent(context, LoggerService.class);
intent.setAction(ACTION_LOG);
intent.putExtra(EXTRA_LOG, log);
context.startService(intent);
}
/**
* Start this service to change the way the service behaves. If
* the service is already performing a task this action will be queued.
*
* @param context
* @param newMode the new mode ordinal to be set
*/
public static void changeMode(Context context, LogMod newMode) {
Intent intent = new Intent(context, LoggerService.class);
intent.setAction(ACTION_SET_MODE);
intent.putExtra(EXTRA_MODE, newMode.ordinal());
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) return;
String action = intent.getAction();
if (action != null) {
if (action.equalsIgnoreCase(ACTION_LOG)) {
String log = intent.getStringExtra(EXTRA_LOG);
if (TextUtils.isEmpty(log)) return;
Log.d(TAG, "mode:"+this.mode+". got log:"+log);
switch(this.mode){
case silent:
writeLogToFile(log);
break;
case active:
sendLogToServer(log);
break;
default:
break;
}
} else if (action.equalsIgnoreCase(ACTION_SET_MODE)) {
int newMode = intent.getIntExtra(EXTRA_MODE, LogMod.silent.ordinal());
setLogMode(LogMod.values()[newMode]);
} else if (action.equalsIgnoreCase(ACTION_CLEANUP)) {
// delete old log file if needed. only keep 7 days of logs
deleteOldLogFile();
}
}
}
private void sendLogToServer(String logStr) {
if (logStr == null) return;
DatagramSocket socket;
InetAddress host;
try {
socket = new DatagramSocket();
if (socket == null) return;
host = InetAddress.getByName(new URL(LOGSTASH_SERVER_URL).getHost());
} catch (SocketException e) {
Log.d(TAG, "couldn't send log:"+e.toString());
return;
} catch (UnknownHostException e) {
Log.d(TAG, "couldn't send log:"+e.toString());
return;
} catch (MalformedURLException e) {
Log.d(TAG, "couldn't send log:"+e.toString());
return;
}
int msg_length = logStr.length();
byte []message = logStr.getBytes();
if (host != null) {
DatagramPacket p = new DatagramPacket(message, msg_length, host, LOGSTASH_UDP_JSON_PORT);
try {
socket.send(p);
} catch (IOException e) {
Log.d(TAG, "couldn't send:"+e.toString());
return;
}
}
}
private void writeLogToFile(String log) {
String dateStr = dateFormat.format(new Date());
String fileName = LOGSTASH_FILE_PREFIX + dateStr;
BufferedWriter bw = null;
try {
FileOutputStream outputStream = openFileOutput(fileName, Context.MODE_APPEND);
DataOutputStream out = new DataOutputStream(outputStream);
bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write(log);
bw.newLine();
} catch (FileNotFoundException e) {
Log.d(TAG, "couldn't write log:"+e.toString());
} catch (IOException e) {
Log.d(TAG, "couldn't write log:"+e.toString());
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
Log.d(TAG, "failed to close BufferedWriter:"+e.toString());
}
}
}
}
private void setLogMode(LogMod newMode) {
if (newMode == this.mode) return;
LogMod oldMode = this.mode;
this.mode = newMode;
if (oldMode == LogMod.silent && newMode == LogMod.active) {
// activating the logging, send all the accumulated logs
flushLogsToServer();
}
}
private void deleteOldLogFile() {
// get the date of MAX_LOG_DAYS days ago
String dateStr = getDayString(-MAX_LOG_DAYS);
// delete the old (week ago) file
String fileName = LOGSTASH_FILE_PREFIX + dateStr;
deleteFile(fileName);
// schedule the logs deletion to occur once a day
setCleanupWakeAlarm(DAY);
}
private String getDayString(int offset) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, offset);
Date newDate = calendar.getTime();
String dateStr = dateFormat.format(newDate);
return dateStr;
}
private void flushLogsToServer() {
// send log file one by one (each log file is a day of logs)
for (int i=MAX_LOG_DAYS; i >= 0; i--) {
String dateStr = getDayString(-i);
String fileName = LOGSTASH_FILE_PREFIX + dateStr;
sendLogFile(fileName);
// delete the log file
deleteFile(fileName);
}
}
/**
* Sends a log file to the server, line by line - each line is a separate log.
* @param fileName log file name
*/
private void sendLogFile(String fileName) {
FileInputStream fstream = null;
try {
fstream = openFileInput(fileName);
} catch (FileNotFoundException e) {
Log.d(TAG, "couldn't open log file"+e.toString());
return;
}
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
try {
String log = "";
while ((log = br.readLine()) != null) {
sendLogToServer(log);
}
} catch (IOException e) {
Log.d(TAG, "couldn't send log to server:"+e.toString());
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
Log.d(TAG, "Failed to close BufferedReader:"+e.toString());
}
}
}
private void setCleanupWakeAlarm(long interval) {
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + interval,
PendingIntent.getBroadcast(this, 0, new Intent(ACTION_CLEANUP), 0));
}
}