Разбор текстового файла по разделителю и вывод нескольких файлов с помощью R - PullRequest
2 голосов
/ 17 марта 2020

Я пытаюсь разбить свой серверный журнал на несколько файлов, чтобы я мог запустить некоторые метрики для них. У меня есть этот cronjob, который добавляет строку и метку времени в журнал сервера первого числа каждого месяца, строка выглядит как «Месячная точка останова, 1 марта 2020 года». Идея состоит в том, что я могу разбить этот большой файл журнала сервера на несколько файлов журнала с помощью этого разделителя строк, а затем запустить некоторые метрики для каждого файла. Я пытаюсь написать скрипт, который будет создавать эти выходные файлы для меня, но я борюсь с этим. Пока что я могу прочитать файл и l oop по строкам и найти разделитель, но я не уверен, что лучший подход для такой проблемы, как эта, может быть, мне не следует использовать R, и есть более простой способ?

# server log
serverLog <- "server-out.log"

# Process File 
conn <- file( serverLog ,open="r")
linn <-readLines(conn)
for (i in 1:length(linn)){
  print( linn[i] )
  test <- grepl(  "Monthly", linn[i] )
  # print( paste("test: ", test, sep="" ) )
  if( test ) {
    print( "Found Monthly Breakpoint")
  }
}
close(conn)

# Example of the server-out.log file 

[0mGET /notifications [36m304 [0m9.439 ms - -[0m
[0mGET /user/status [36m304 [0m2.137 ms - -[0m
[0mGET /user/status [36m304 [0m5.675 ms - -[0m
[0mPOST /user/login [32m200 [0m19.960 ms - 30[0m
[0mGET /user/status [36m304 [0m9.518 ms - -[0m
[0mGET /user/status [32m200 [0m2.364 ms - 16[0m
[0mGET /user/status [36m304 [0m1.396 ms - -[0m
[0mGET /user/status [36m304 [0m1.087 ms - -[0m
[0mPOST /user/login [32m200 [0m300.214 ms - 30[0m
[0mGET /user/status [36m304 [0m4.374 ms - -[0m
[0mGET /localUser [32m200 [0m2.260 ms - 1045[0m

 Monthly Breakpoint, March 1 2020

[0mGET /user/status [32m200 [0m5.284 ms - 16[0m
[0mGET /user/status [36m304 [0m2.101 ms - -[0m
[0mGET /users [32m200 [0m2.387 ms - 36[0m
[0mGET /notifications [32m200 [0m30.395 ms - 2624[0m
[0mGET /user/status [36m304 [0m2.172 ms - -[0m
[0mGET /user/status [36m304 [0m1.424 ms - -[0m
[0mGET /user/status [36m304 [0m2.074 ms - -[0m
[0mGET /user/status [36m304 [0m0.920 ms - -[0m
[0mGET /users [36m304 [0m2.471 ms - -[0m
[0mGET /notifications [36m304 [0m8.416 ms - -[0m
[0mGET /user/status [36m304 [0m1.757 ms - -[0m
[0mGET /user/status [36m304 [0m1.114 ms - -[0m
[0mGET /favicon.ico [33m404 [0m2.218 ms - 150[0m
[0mGET /user/status [36m304 [0m2.003 ms - -[0m
[0mPOST /user/login [32m200 [0m175.473 ms - 30[0m
[0mGET /user/status [36m304 [0m3.893 ms - -[0m
  • Обновление Я попытался использовать csplit, потому что это звучит как хороший вариант для этой проблемы, но я тоже не могу заставить это работать ... Можете ли вы привести пример?
csplit -z server-out.min /Monthly/ '{*}'

csplit: illegal option -- z
usage: csplit [-ks] [-f prefix] [-n number] file args ...

Ответы [ 3 ]

1 голос
/ 17 марта 2020

Вероятно, использование некоторых UNIX команд будет наиболее "естественным", awk и csplit будут работать в этом отношении .

В любом случае, у меня есть решение R , Вместо использования readLines() я бы начал с read.delim(). Таким образом, вы начинаете с data.frame, а затем можете использовать любые инструменты для манипуляции data.frame. Я больше всего знаком с командами tidyverse, поэтому я бы использовал их здесь.

# Process File 
library(tidyverse)
log_df <- read.delim(serverLog, header = FALSE) %>% 
  mutate(breakpoint = grepl("Monthly Breakpoint", V1),
         breakdate = ifelse(breakpoint, gsub("Monthly Breakpoint, ", "", V1), NA)) %>% 
  fill(breakdate) %>% 
  mutate(breakdate = ifelse(is.na(breakdate), "before first breakdate", breakdate)) %>% 
  filter(!breakpoint) %>% 
  select(-breakpoint)

# Save Files
log_df %>% 
  split(.$breakdate) %>% 
  lapply(function(x) write.csv(x, file = paste(x$breakdate[1], ".csv"), row.names = FALSE))

Я не знаю, хотя, если хранение данных в отдельных файлах - лучший рабочий процесс, чтобы выбрать здесь. Почему бы просто не сохранить данные в R, разбить строки на несколько столбцов и сгруппировать анализ по месяцам.

РЕДАКТИРОВАТЬ: Вот как может выглядеть разбиение на столбцы и некоторый анализ.

# split / separate() into columns

log_sep_df <- 
  log_df %>%
  as_tibble() %>% 
  mutate(V1 = substr(V1, 2, nchar(as.character(V1)))) %>% 
  separate(V1, into = c(paste0("var", 1:10)), sep = "\\[|  | ") %>% 
  mutate(http = ifelse(grepl("POST", var1), "POST", "GET")) %>% 
  mutate(var1 = gsub("POST|GET", "", var1))

# get month labels
library(lubridate)
log_sep_df <- 
log_sep_df %>% 
  mutate(date = as.Date(mdy(log_sep_df$breakdate)))

date_before_first_breakpoint <- min(log_sep_df$date, na.rm = TRUE) - 10

log_sep_df <- 
log_sep_df %>% 
  mutate(date = if_else(is.na(date), 
                        date_before_first_breakpoint, 
                        date),
         month = month(date, label = TRUE))


# grouped visiualization of logs
ggplot(log_sep_df, aes(http)) +
  geom_bar() +
  facet_wrap(~month)
0 голосов
/ 17 марта 2020

Если вы хотите сделать это в R, вы можете использовать data.table решение для эффективности:

library(data.table)
DT <- fread("out.log", sep = NULL, header = FALSE)[V1 != ""]
DT[, Idx := rleid(grepl("Monthly Breakpoint", V1))]
DT <- DT[!grepl("Monthly Breakpoint", V1)]
DT.list <- split(DT, DT$Idx) ## or just operate by Idx
0 голосов
/ 17 марта 2020

Это не самый элегантный ответ, но это дало мне то, что мне было нужно. Я попробую другой ответ, это хорошая идея, чтобы сохранить данные в моей среде R, чтобы я мог запускать все свои метрики, не считывая ненужные файлы. Спасибо @Till

#~~~~~~~~~~~~~~~~~~~~~~#
#~~ Parse Server Log ~~#
#~~~~~~~~~~~~~~~~~~~~~~#

# Read File 
serverLog <- "server-out.min"
conn <- file( serverLog ,open="r")
linn <-readLines(conn)
num <- 1

# Loop through File 
for (i in 1:length(linn)){
  # print( linn[i] )

  # current output file
  file <- paste( "server-log-", num, sep = "")
  # write to file
  write(linn[i], file=file, append=TRUE)

  # Check for Monthly Delimiter, update num
  test <- grepl(  "Monthly", linn[i] )
  if( test ) {
    print( "Found Monthly Breakpoint")
    num <- num+1
  }
}
close(conn)
...