Explorar o código

logfile rotating implemented

Jurij Retzlaff %!s(int64=5) %!d(string=hai) anos
pai
achega
acc7253ca1

+ 54 - 31
code/lib/jomjol_fileserver_ota/server_file.cpp

@@ -84,7 +84,7 @@ static esp_err_t favicon_get_handler(httpd_req_t *req)
  * a list of all files and folders under the requested path.
  * In case of SPIFFS this returns empty list when path is any
  * string other than '/', since SPIFFS doesn't support directories */
-static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
+static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const char* uripath, bool readonly)
 {
     char entrypath[FILE_PATH_MAX];
     char entrysize[16];
@@ -120,24 +120,24 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
     httpd_resp_sendstr_chunk(req, "<!DOCTYPE html><html><body>");
 
 /////////////////////////////////////////////////
-
-    FILE *fd = fopen("/sdcard/html/upload_script.html", "r");
-    char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
-    size_t chunksize;
-    do {
-        chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);
-//        printf("Chunksize %d\n", chunksize);
-        if (chunksize > 0){
-            if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
-            fclose(fd);
-            ESP_LOGE(TAG, "File sending failed!");
-            return ESP_FAIL;
+    if (!readonly) {
+        FILE *fd = fopen("/sdcard/html/upload_script.html", "r");
+        char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
+        size_t chunksize;
+        do {
+            chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);
+            //        printf("Chunksize %d\n", chunksize);
+            if (chunksize > 0){
+                if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
+                fclose(fd);
+                ESP_LOGE(TAG, "File sending failed!");
+                return ESP_FAIL;
+                }
             }
-        }
-    } while (chunksize != 0);
-    fclose(fd);
-//    ESP_LOGI(TAG, "File sending complete");
-
+        } while (chunksize != 0);
+        fclose(fd);
+        //    ESP_LOGI(TAG, "File sending complete");
+    }
 ///////////////////////////////
 
     std::string _zw = std::string(dirpath);
@@ -149,12 +149,16 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
     httpd_resp_sendstr_chunk(req,
         "<table class=\"fixed\" border=\"1\">"
         "<col width=\"800px\" /><col width=\"300px\" /><col width=\"300px\" /><col width=\"100px\" />"
-        "<thead><tr><th>Name</th><th>Type</th><th>Size (Bytes)</th><th>Delete<br>"
-        "<form method=\"post\" action=\"");
-    httpd_resp_sendstr_chunk(req, _zw.c_str());
-    httpd_resp_sendstr_chunk(req,          
-        "\"><button type=\"submit\">DELETE ALL!</button></form>"
-        "</th></tr></thead><tbody>\n");
+        "<thead><tr><th>Name</th><th>Type</th><th>Size (Bytes)</th>");
+    if (!readonly) {
+        httpd_resp_sendstr_chunk(req, "<th>Delete<br>"
+            "<form method=\"post\" action=\"");
+        httpd_resp_sendstr_chunk(req, _zw.c_str());
+        httpd_resp_sendstr_chunk(req,
+            "\"><button type=\"submit\">DELETE ALL!</button></form>"
+            "</th></tr>");
+    }
+    httpd_resp_sendstr_chunk(req, "</thead><tbody>\n");
 
     /* Iterate over all files / folders and fetch their names and sizes */
     while ((entry = readdir(dir)) != NULL) {
@@ -173,7 +177,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
 
             /* Send chunk of HTML file containing table entries with file name and size */
             httpd_resp_sendstr_chunk(req, "<tr><td><a href=\"");
-            httpd_resp_sendstr_chunk(req, req->uri);
+            httpd_resp_sendstr_chunk(req, "/fileserver");
+            httpd_resp_sendstr_chunk(req, uripath);
             httpd_resp_sendstr_chunk(req, entry->d_name);
             if (entry->d_type == DT_DIR) {
                 httpd_resp_sendstr_chunk(req, "/");
@@ -184,11 +189,13 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
             httpd_resp_sendstr_chunk(req, entrytype);
             httpd_resp_sendstr_chunk(req, "</td><td>");
             httpd_resp_sendstr_chunk(req, entrysize);
-            httpd_resp_sendstr_chunk(req, "</td><td>");
-            httpd_resp_sendstr_chunk(req, "<form method=\"post\" action=\"/delete");
-            httpd_resp_sendstr_chunk(req, req->uri + strlen("/fileserver"));
-            httpd_resp_sendstr_chunk(req, entry->d_name);
-            httpd_resp_sendstr_chunk(req, "\"><button type=\"submit\">Delete</button></form>");
+            if (!readonly) {
+                httpd_resp_sendstr_chunk(req, "</td><td>");
+                httpd_resp_sendstr_chunk(req, "<form method=\"post\" action=\"/delete");
+                httpd_resp_sendstr_chunk(req, uripath);
+                httpd_resp_sendstr_chunk(req, entry->d_name);
+                httpd_resp_sendstr_chunk(req, "\"><button type=\"submit\">Delete</button></form>");
+            }
             httpd_resp_sendstr_chunk(req, "</td></tr>\n");
         }
     }
@@ -226,6 +233,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
 //    filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
 //                                             req->uri, sizeof(filepath));
 
+
     if (!filename) {
         ESP_LOGE(TAG, "Filename is too long");
         /* Respond with 500 Internal Server Error */
@@ -235,7 +243,22 @@ static esp_err_t download_get_handler(httpd_req_t *req)
 
     /* If name has trailing '/', respond with directory contents */
     if (filename[strlen(filename) - 1] == '/') {
-        return http_resp_dir_html(req, filepath);
+        bool readonly = false;
+        size_t buf_len = httpd_req_get_url_query_len(req) + 1;
+        if (buf_len > 1) {
+            char buf[buf_len];
+            if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
+                ESP_LOGI(TAG, "Found URL query => %s", buf);
+                char param[32];
+                /* Get value of expected key from query string */
+                if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
+                    ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
+                    readonly = param && strcmp(param,"true")==0;
+                }
+            }
+        }
+
+        return http_resp_dir_html(req, filepath, filename, readonly);
     }
 
     std::string testwlan = toUpper(std::string(filename));

+ 4 - 0
code/lib/jomjol_flowcontroll/ClassFlowControll.cpp

@@ -280,6 +280,10 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
             {
                 LogFile.SwitchOnOff(false);
             }
+        }
+        if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
+        {
+            LogFile.SetRetention(std::stoi(zerlegt[1]));
         }      
     }
     return true;

+ 0 - 2
code/lib/jomjol_flowcontroll/ClassFlowMakeImage.h

@@ -4,8 +4,6 @@
 
 #include <string>
 
-static const char* TAG2 = "example";
-
 #define BLINK_GPIO GPIO_NUM_4
 
 #define CAMERA_MODEL_AI_THINKER

+ 67 - 0
code/lib/jomjol_helper/Helper.cpp

@@ -1,8 +1,12 @@
 //#pragma warning(disable : 4996)
 
 #include "Helper.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
 
 //#define ISWINDOWS_TRUE
+#define PATH_MAX_STRING_SIZE 256
 
 using namespace std;
 
@@ -159,6 +163,63 @@ string getFileType(string filename)
 	return zw;
 }
 
+/* recursive mkdir */
+int mkdir_r(const char *dir, const mode_t mode) {
+    char tmp[PATH_MAX_STRING_SIZE];
+    char *p = NULL;
+    struct stat sb;
+    size_t len;
+    
+    /* copy path */
+    len = strnlen (dir, PATH_MAX_STRING_SIZE);
+    if (len == 0 || len == PATH_MAX_STRING_SIZE) {
+        return -1;
+    }
+    memcpy (tmp, dir, len);
+    tmp[len] = '\0';
+
+    /* remove trailing slash */
+    if(tmp[len - 1] == '/') {
+        tmp[len - 1] = '\0';
+    }
+
+    /* check if path exists and is a directory */
+    if (stat (tmp, &sb) == 0) {
+        if (S_ISDIR (sb.st_mode)) {
+            return 0;
+        }
+    }
+    
+    /* recursive mkdir */
+    for(p = tmp + 1; *p; p++) {
+        if(*p == '/') {
+            *p = 0;
+            /* test path */
+            if (stat(tmp, &sb) != 0) {
+                /* path does not exist - create directory */
+                if (mkdir(tmp, mode) < 0) {
+                    return -1;
+                }
+            } else if (!S_ISDIR(sb.st_mode)) {
+                /* not a directory */
+                return -1;
+            }
+            *p = '/';
+        }
+    }
+    /* test path */
+    if (stat(tmp, &sb) != 0) {
+        /* path does not exist - create directory */
+        if (mkdir(tmp, mode) < 0) {
+            return -1;
+        }
+    } else if (!S_ISDIR(sb.st_mode)) {
+        /* not a directory */
+        return -1;
+    }
+    return 0;
+}
+
 string toUpper(string in)
 {
 	for (int i = 0; i < in.length(); ++i)
@@ -173,3 +234,9 @@ float temperatureRead()
 {
     return (temprature_sens_read() - 32) / 1.8;
 }
+
+time_t addDays(time_t startTime, int days) {
+	struct tm* tm = localtime(&startTime);
+	tm->tm_mday += days;
+	return mktime(tm);
+}

+ 4 - 0
code/lib/jomjol_helper/Helper.h

@@ -17,6 +17,10 @@ bool ctype_space(const char c, string adddelimiter);
 
 string getFileType(string filename);
 
+int mkdir_r(const char *dir, const mode_t mode);
+
 string toUpper(string in);
 
 float temperatureRead();
+
+time_t addDays(time_t startTime, int days);

+ 98 - 19
code/lib/jomjol_logfile/ClassLogFile.cpp

@@ -1,7 +1,14 @@
 #include "ClassLogFile.h"
 #include "time_sntp.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include "Helper.h"
 
-ClassLogFile LogFile("/sdcard/log.txt");
+static const char *TAG = "log";
+
+ClassLogFile LogFile("/sdcard/log/message", "log_%Y-%m-%d.txt");
 
 void ClassLogFile::WriteToDedicatedFile(std::string _fn, std::string info, bool _time)
 {
@@ -13,39 +20,111 @@ void ClassLogFile::WriteToDedicatedFile(std::string _fn, std::string info, bool
     }
 
     pFile = fopen(_fn.c_str(), "a+");
+    if (pFile!=NULL) {
+        if (_time)
+        {
+            time_t rawtime;
+            struct tm* timeinfo;
+            char buffer[80];
 
-    if (_time)
-    {
-        time_t rawtime;
-        struct tm* timeinfo;
-        char buffer[80];
+            time(&rawtime);
+            timeinfo = localtime(&rawtime);
 
-        time(&rawtime);
-        timeinfo = localtime(&rawtime);
+            strftime(buffer, 80, "%Y-%m-%d_%H-%M-%S", timeinfo);
 
-        strftime(buffer, 80, "%Y-%m-%d_%H-%M-%S", timeinfo);
+            zwtime = std::string(buffer);
+            info = zwtime + ": " + info;
+        }
+        fputs(info.c_str(), pFile);
+        fputs("\n", pFile);
 
-        zwtime = std::string(buffer);
-        info = zwtime + ": " + info;
+        fclose(pFile);    
+    } else {
+        ESP_LOGI(TAG, "Can't open log file %s", _fn.c_str());
     }
-    fputs(info.c_str(), pFile);
-    fputs("\n", pFile);
-
-    fclose(pFile);    
 }
 
 void ClassLogFile::SwitchOnOff(bool _doLogFile){
     doLogFile = _doLogFile;
 };
 
+void ClassLogFile::SetRetention(unsigned short _retentionInDays){
+    retentionInDays = _retentionInDays;
+};
 
 void ClassLogFile::WriteToFile(std::string info, bool _time)
 {
-    WriteToDedicatedFile(logfile, info, _time);
+    struct stat path_stat;
+    if (stat(logroot.c_str(), &path_stat) != 0) {
+        ESP_LOGI(TAG, "Create log folder: %s", logroot.c_str());
+        if (mkdir_r(logroot.c_str(), S_IRWXU) == -1)  {
+            ESP_LOGI(TAG, "Can't create log foolder");
+        }
+    }
+
+    time_t rawtime;
+    struct tm* timeinfo;
+    char buffer[30];
+
+    time(&rawtime);
+    timeinfo = localtime(&rawtime);
+
+    strftime(buffer, 30, logfile.c_str(), timeinfo);
+    std::string logpath = logroot + "/" + buffer; 
+    
+    WriteToDedicatedFile(logpath, info, _time);
+}
+
+void ClassLogFile::RemoveOld()
+{
+    if (retentionInDays == 0) {
+        return;
+    }
+
+    time_t rawtime;
+    struct tm* timeinfo;
+    char cmpfilename[30];
+
+    time(&rawtime);
+    rawtime = addDays(rawtime, -retentionInDays);
+    timeinfo = localtime(&rawtime);
+    
+    strftime(cmpfilename, 30, logfile.c_str(), timeinfo);
+    //ESP_LOGE(TAG, "log file name to compare: %s", cmpfilename);
+
+    DIR *dir = opendir(logroot.c_str());
+    if (!dir) {
+        ESP_LOGE(TAG, "Failed to stat dir : %s", logroot.c_str());
+        return;
+    }
+
+    struct dirent *entry;
+    int deleted = 0;
+    int notDeleted = 0;
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_type == DT_REG) {
+            //ESP_LOGE(TAG, "list log file : %s", entry->d_name);
+            if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
+                ESP_LOGE(TAG, "delete log file : %s", entry->d_name);
+                std::string filepath = logroot + "/" + entry->d_name; 
+                if (unlink(filepath.c_str()) == 0) {
+                    deleted ++;
+                } else {
+                    ESP_LOGE(TAG, "can't delete file : %s", entry->d_name);
+                }
+            } else {
+                notDeleted ++;
+            }
+        }
+    }
+    ESP_LOGE(TAG, "%d older log files deleted. %d current log files not deleted.", deleted, notDeleted);
+    closedir(dir);
 }
 
-ClassLogFile::ClassLogFile(std::string _logfile)
+ClassLogFile::ClassLogFile(std::string _logroot, std::string _logfile)
 {
-    logfile = _logfile; 
+    logroot = _logroot;
+    logfile =  _logfile;
     doLogFile = true;
-}
+    retentionInDays = 10;
+}

+ 5 - 1
code/lib/jomjol_logfile/ClassLogFile.h

@@ -5,15 +5,19 @@
 class ClassLogFile
 {
 private:
+    std::string logroot;
     std::string logfile;
     bool doLogFile;
+    unsigned short retentionInDays;
 public:
-    ClassLogFile(std::string _logfile);
+    ClassLogFile(std::string _logpath, std::string _logfile);
 
     void SwitchOnOff(bool _doLogFile);
+    void SetRetention(unsigned short _retentionInDays);
 
     void WriteToFile(std::string info, bool _time = true);
     void WriteToDedicatedFile(std::string _fn, std::string info, bool _time = true);
+    void RemoveOld();
 };
 
 extern ClassLogFile LogFile;

+ 0 - 2
code/src/server_main.h

@@ -14,8 +14,6 @@
 
 #include <esp_http_server.h>
 
-static const char *TAG = "example";
-
 extern httpd_handle_t server;
 
 httpd_handle_t start_webserver(void);

+ 2 - 0
code/src/server_tflite.cpp

@@ -433,6 +433,8 @@ void task_autodoFlow(void *pvParameter)
             printf("Autoflow: doFLow wird gestartet\n");
             flowisrunning = true;
             doflow();
+            printf("Remove older log files\n");
+            LogFile.RemoveOld();
         }
         
         LogFile.WriteToFile("task_autodoFlow - round done");

+ 2 - 2
code/src/version.cpp

@@ -1,4 +1,4 @@
-const char* GIT_REV="2ab2f07";
+const char* GIT_REV="05a0f6f";
 const char* GIT_TAG="";
 const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2020-11-03 22:12";
+const char* BUILD_TIME="2020-11-06 17:57";

+ 2 - 2
code/version.cpp

@@ -1,4 +1,4 @@
-const char* GIT_REV="2ab2f07";
+const char* GIT_REV="05a0f6f";
 const char* GIT_TAG="";
 const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2020-11-03 22:12";
+const char* BUILD_TIME="2020-11-06 17:57";

+ 2 - 0
sd-card/config/config.ini

@@ -51,5 +51,7 @@ Intervall = 4.85
 
 [Debug]
 Logfile = False
+; Number of days before a log file is deleted. 0 = disabled. 10 is default value (if not defined)
+;LogfileRetentionInDays = 10
 
 [Ende]

+ 1 - 1
sd-card/html/index.html

@@ -91,7 +91,7 @@ li.dropdown {
     <a href="javascript:void(0)" class="dropbtn">System</a>
     <div class="dropdown-content">
       <a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
-      <a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log.txt';">Log Viewer</a>      
+      <a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>      
       <a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
       <a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>      
     </div>