/*--------------------------------------------------------------------------- logger.c Markus Zolliker, Sept 2004 ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include "splitter.h" #include "logger.h" #ifndef TECS_LOG #include "sics.h" #endif #define LOGGER_NAN -999999. #define ONE_YEAR (366*24*3600) struct Logger { char *name; char *old; int oldsize; int period; time_t last; int lastLn; /* 0 or the number chars counted from the second last newline character. */ int overwrite; /* overwrite last line when value has not changed */ int oldValue; /* old value to be overwritten */ int numeric; int exact; Logger *next; }; static char *dir = NULL; static Logger *list; static time_t lastLife = 0; /*--------------------------------------------------------------------------*/ char *LoggerName(Logger *log) { return log->name; } /*--------------------------------------------------------------------------*/ void LoggerSetNumeric(Logger *log, int numeric) { if (log) { log->numeric = numeric; } } /*--------------------------------------------------------------------------*/ Logger *LoggerFind(const char *name) { Logger *p; p = list; while (p != NULL) { if (0==strcasecmp(name, p->name)) { return p; } p = p->next; } return NULL; } /*--------------------------------------------------------------------------*/ #define LASTLOGTXT "#last logging entry at:\n" void LoggerSetDir(char *dirarg) { char path[256], line[32]; FILE *fil; dir = dirarg; snprintf(path, sizeof path, "%s/lastlife.dat", dir); fil = fopen(path, "r"); if (fil) { fgets(line, sizeof line, fil); if (strcmp(line, LASTLOGTXT) == 0) { fgets(line, sizeof line, fil); lastLife = atol(line); if (lastLife < 1000000000) { printf("bad lastLife %ld\n", (long)lastLife); } } fclose(fil); } else { perror("open read error \n"); } } /*--------------------------------------------------------------------------*/ char *LoggerGetDir(void) { char path[256]; FILE *fil; time_t now; static time_t last; now = time(NULL); if (now != last) { snprintf(path, sizeof path, "%s/lastlife.dat", dir); fil = fopen(path, "w"); if (fil) { fprintf(fil, "%s%ld\n", LASTLOGTXT, (long)now); fclose(fil); } else { printf("can not open %s\n", path); perror("open write error \n"); } last = now; } return dir; } /*--------------------------------------------------------------------------*/ int LoggerVarPath(char *dir, char *path, int pathLen, char *name) { int l; l = strlen(dir); if (l + strlen(name) + 2 >= pathLen) { path[0]='\0'; return 0; } strcpy(path, dir); path[l] = '/'; l++; for (;*name != '\0'; name++, l++) { path[l] = tolower(*name); } path[l] = '/'; l++; path[l] = '\0'; return l; } /*--------------------------------------------------------------------------*/ int LoggerWrite0(Logger *log, time_t now, int period, char *value) { char path[256], stim[32], buf[32], chr; struct tm *tm; int yday; int isdst; int l, ext, skip, changed; FILE *fil; long endPos, pos1, p; LoggerGetDir(); if (dir == NULL) return 0; if (now == 0) { printf("now==0\n"); } tm = localtime(&log->last); isdst = tm->tm_isdst; yday = tm->tm_yday; tm = localtime(&now); if (tm->tm_yday != yday) { log->period = -1; } l = LoggerVarPath(dir, path, sizeof path, log->name); strftime(path + l, sizeof path - l, "%m-%d.log", tm); strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", tm); fil = fopen(path, "r+"); skip = 0; if (fil == NULL) { /* create new file */ fil = fopen(path, "w+"); if (fil == NULL) return 0; fprintf(fil, "%s isdst %d period %d exact %d\n", stim, tm->tm_isdst, period, log->exact); endPos = ftell(fil); fseek(fil, -1, SEEK_CUR); } else { /* check if file is from today */ fgets(buf, sizeof buf, fil); if (0 != strncmp(buf, stim, 11)) { fclose(fil); fil=fopen(path, "w+"); /* overwrite old logfile */ if (fil == NULL) return 0; fputs(stim, fil); endPos = ftell(fil); fseek(fil, -1, SEEK_CUR); log->period = -1; } else { fseek(fil, -log->lastLn, SEEK_END); /* set position to last line */ endPos = ftell(fil) + log->lastLn; if (endPos > 0 && log->lastLn == 0) { /* find last newline in newly opened file */ fseek(fil, -1, SEEK_CUR); chr = fgetc(fil); while (chr != '\n' && ftell(fil) >= 2) { fseek(fil, -2, SEEK_CUR); chr = fgetc(fil); } fseek(fil, -1, SEEK_CUR); } else if (now != log->last) { skip = 1; } } } changed = (0 != strcmp(value, log->old)); if (period != log->period) { log->period = period; if (log->numeric) { snprintf(buf, sizeof buf, "\t%d", period); } else { strcpy(buf, "\t0"); } changed = 1; } else { buf[0]='\0'; if (log->overwrite && !changed) { skip = 0; } } pos1 = 0; chr = fgetc(fil); while (chr != EOF) { if (chr == '\n') { skip--; if (skip < 0) break; } chr = fgetc(fil); } if (chr == EOF) { if (pos1 == 0) { fputs("\n#no newline found\n", fil); } else { fseek(fil, pos1, SEEK_SET); } } if (tm->tm_isdst != isdst) { fprintf(fil, "#isdst %d\n", tm->tm_isdst); } pos1 = ftell(fil); strftime(stim, sizeof stim,"%H:%M:%S", tm); fprintf(fil, "%s\t%s%s\n", stim, value, buf); for (p = ftell(fil); p < endPos; p++) { /* overwrite dirt after last line */ fprintf(fil, " "); } endPos = ftell(fil); fclose(fil); log->lastLn = (endPos - pos1) + 1; /* overwrite next time when value has not changed */ log->overwrite = ! changed; l = strlen(value); if (l >= log->oldsize) { ext = ((l - log->oldsize)/16 + 1) * 16; if (ext < log->oldsize / 4) ext += (log->oldsize / 64) * 16; log->oldsize += ext; free(log->old); log->old = calloc(1, log->oldsize); assert(log->old); assert(l < log->oldsize); } strcpy(log->old, value); log->old[l] = '\0'; log->last = now; return 1; } /*--------------------------------------------------------------------------*/ int LoggerWrite(Logger *log, time_t now, int period, char *value) { Logger *p; time_t h0; static int yday = -1; struct tm *tm; int l; FILE *fil; char path[PATH_MAX]; char tim[256]; tm = localtime(&now); if (tm->tm_yday != yday) { tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; h0 = mktime(tm); yday = tm->tm_yday; l = LoggerVarPath(dir, path, sizeof path, "debug"); strftime(path + l, sizeof path - l, "%m-%d.log", tm); fil=fopen(path, "a"); if (fil) { strftime(tim, sizeof tim, "h0 %m-%d %H:%M:%S\n", localtime(&h0)); fputs(tim, fil); } /* log old values (forced midnight log) */ p = list; while (p != NULL) { if (fil) { strftime(tim, sizeof tim, "last %m-%d %H:%M:%S, ", localtime(&p->last)); fputs(tim, fil); fprintf(fil, "period %d, name %s, old %s\n", p->period, p->name, p->old); } if (p->last < h0 && p->last != 0 && p->period >= 0 && p->old[0] != '\0') { LoggerWrite0(p, h0, p->period, p->old); p->overwrite = 0; } p = p->next; } if (fil) fclose(fil); } return LoggerWrite0(log, now, period, value); } /*--------------------------------------------------------------------------*/ time_t LoggerLastTime(Logger *log) { if (log->last != 0 && log->period > 0) { return log->last; } return 0; } /*--------------------------------------------------------------------------*/ int LoggerGetValue(void *data, char *value, int size) { Logger *log = data; value[0]='\0'; strncat(value, log->old, size - 1); return 1; } /*--------------------------------------------------------------------------*/ void LoggerKill(Logger *log) { /* we do not really free the logger, it might be reused for the same variable later. We set the value to undefined */ if (list != NULL) { /* LoggerFreeAll not yet called */ LoggerWrite(log, time(NULL), 0, ""); } } /*--------------------------------------------------------------------------*/ Logger *LoggerMake(char *name, int period, int exact) { Logger *log; char path[256]; struct stat st; int i; time_t t; LoggerGetDir(); if (dir == NULL) return NULL; LoggerVarPath(dir, path, sizeof path, name); i = stat(path, &st); if (i >= 0) { if (((st.st_mode >> 12) & 15) != 4) { /* exists, but is no directory */ return NULL; } } else { i = mkdir(path, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); if (i < 0) return NULL; /* mkdir failed */ } log = LoggerFind(name); /* look if logger already exists */ if (log == NULL) { log = calloc(1, sizeof *log); if (log == NULL) return NULL; log->name = strdup(name); if (log->name == NULL) { free(log); return NULL; } log->period = -1; log->exact = exact; log->old = calloc(1,12); log->oldsize = 12; log->last = 0; log->lastLn = 0; log->numeric = 1; log->next = list; list = log; t = time(NULL) -1; if (lastLife != 0 && lastLife + period < t) { t = lastLife + period; } LoggerWrite(log, t, period, ""); /* value was undefined since last life of server */ } return log; } /*--------------------------------------------------------------------------*/ #ifndef TECS_LOG typedef enum { NUMERIC, TEXT } CompType; typedef struct { SConnection *pCon; char *var; int exact; CompType type; time_t step; time_t tlim; time_t tmin, tmax, tlast, told; float ymin, ymax, ylast, yold; char slast[256]; char set[256]; int np; } Compressor; static char *dir2 = NULL; static void LoggerInitC(Compressor *c, SConnection *pCon, time_t step) { c->pCon = pCon; c->step = step; c->tmin = -2; c->tmax = 0; c->tlast = 0; c->tlim = 0; c->told = 0; c->slast[0]='\0'; c->ylast = LOGGER_NAN; } static void LoggerOutStr(Compressor *c, time_t t, char *str) { char line[256]; /* printf("out %ld %g\n", t, y); */ if (0 != strcmp(str, c->slast)) { snprintf(line, sizeof line, "%ld %s\n", (long)(t - c->tlast), str); c->tlast = t; c->slast[0]='\0'; strncat(c->slast, str, sizeof c->slast - 1); SCWrite(c->pCon, line, eWarning); c->np--; } } static void LoggerOut(Compressor *c, time_t t, float y) { char line[80]; /* printf("out %ld %g\n", t, y); */ if (y != c->ylast) { c->ylast = y; if (y == LOGGER_NAN) { snprintf(line, sizeof line, "%ld\n", (long)(t - c->tlast)); } else { snprintf(line, sizeof line, "%ld %g\n", (long)(t - c->tlast), y); } /* printf("-%s\n", line); */ c->tlast = t; SCWrite(c->pCon, line, eWarning); c->np--; } } static void LoggerPut(Compressor *c, time_t t, char *value) { char *p; double y; if (c->var) { SCPrintf(c->pCon, eWarning, "*%s exact %d\n", c->var, c->exact); c->var = NULL; } if (c->type == NUMERIC) { if (t == 0) { /* finish */ t = c->tlim + 3 * c->step; y = 0; } else { y = strtod(value, &p); if (p == value) { y = LOGGER_NAN; } else { if (y == LOGGER_NAN) y *= 1.0000002; } } /* printf("put %ld %g\n", t, y); */ if (c->tlim == 0) goto first; if (t >= c->tlim) { c->tlim += c->step; if (c->tmin > c->tmax) { LoggerOut(c, c->tmax, c->ymax); c->ymax = c->ymin; c->tmax = c->tmin; } else { LoggerOut(c, c->tmin, c->ymin); if (c->tmin == c->tmax) goto first; c->ymin = c->ymax; c->tmin = c->tmax; } if (t >= c->tlim) { LoggerOut(c, c->tmin, c->ymin); if (t >= c->tlim + c->step) { LoggerOut(c, c->told, c->yold); } goto first; } } if (y <= c->ymin) { c->ymin = y; c->tmin = t; } else if (y > c->ymax) { c->ymax = y; c->tmax = t; } c->yold = y; c->told = t; return; first: c->tlim = t + 2 * c->step; c->tmin = t; c->tmax = t; c->ymin = y; c->ymax = y; return; } else if (c->type == TEXT) { if (t != 0) LoggerOutStr(c, t, value); } } /*--------------------------------------------------------------------------*/ int LoggerGraph(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { time_t from, to, step, xs, lastt, now; long lxs; char *p; int i, iarg0, l, iret, loss, np; int inRange; int yday=0; time_t t, startim; struct tm tm; char stim[32], path[256], line[256], lastval[256]; char *lin, *val, *stp; FILE *fil; Compressor c; float yy, lasty; CompType type0; DIR *dr; char *opt; int isdst; int overflow; /* argtolower(argc, argv); */ if (argc < 4) goto illarg; now = time(NULL); from = strtol(argv[1], &p, 0); /* unix time, not year 2038 safe */ if (p == argv[1]) goto illarg; to = strtol(argv[2], NULL, 0); if (p == argv[2]) goto illarg; if (from < ONE_YEAR) { from += now; } if (to < ONE_YEAR) { to += now; } iarg0 = 4; if (strcasecmp(argv[3],"text") == 0) { /* non-numeric values */ step = 1; type0 = TEXT; np = from - to + 2; } else if (strcasecmp(argv[3],"np") == 0) { /* max. number of points */ type0 = NUMERIC; iarg0 = 5; np = strtol(argv[4], NULL, 0); if (to <= from) { step = 1; } else if (np <= 2) { step = to - from; } else { step = (to - from) / (np - 2) + 1; } } else { step = strtol(argv[3], NULL, 0); if (step <= 0) step = 1; type0 = NUMERIC; np = (from - to) / step + 2; } if (p == argv[3]) goto illarg; if (step <= 0) step = 1; snprintf(line, sizeof line, "%ld\n", (long)now); SCWrite(pCon, line, eWarning); if (dir == NULL) { dir = IFindOption(pSICSOptions, "LoggerDir"); if (dir == NULL) { SCWrite(pCon, "LoggerDir not found", eError); return 0; } LoggerSetDir(dir); } if (dir2 == NULL) { dir2 = IFindOption(pSICSOptions, "LoggerDir2"); if (dir2 == NULL) dir2=""; } loss = 0; overflow = 0; for (i=iarg0; inext; if (p->name) free(p->name); if (p->old) free(p->old); free(p); p = next; } list = NULL; } /*--------------------------------------------------------------------------*/ void LoggerInit(void) { AddCommand(pServ->pSics,"Graph",LoggerGraph,LoggerFreeAll,NULL); } #endif /* TECS_LOG */