Skip to content

Instantly share code, notes, and snippets.

@dant3
Last active July 21, 2021 06:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dant3/0fbc3aa876ca13b199a2 to your computer and use it in GitHub Desktop.
Save dant3/0fbc3aa876ca13b199a2 to your computer and use it in GitHub Desktop.
Logstash Android client
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://localhost/";
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));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment