LINUX.ORG.RU

Раскидать логи по каталогам в зависимости от даты

 ,


1

1

Есть десятки тысяч постоянно добавляющихся файлов логов с именами в виде app_id1_YYYYMMDDHHMMSS_id2 и есть каталоги вида YYYYMMDD в которые эти логи надо раскидывать для дальнейшей обработки
Файлов очень много, поэтому мучает меня любопытство о самом быстром способе сопоставления файлов по дате и перекладывании файлов в соответствующие каталоги.
Доп. ограничение: изначально файлы кладутся именно в один каталог, изменить это я не в силах.

Дополнение: Делать это нужно с минимальными задержками, то есть разбор на следующий день не подходит

★★★★★

Последнее исправление: zolden (всего исправлений: 1)

Если их имя соотвествует фактическому отпечатку даты, то можно делать выборку find'ом, начиная с самой древней существующей даты, и инкриментируя по одному дню. Полученные блоки перемещать в созданный для них каталог.

Spirit_of_Stallman ★★★
()
Ответ на: комментарий от Spirit_of_Stallman

Да,это конечно вариант и я его попробую реализовать, но по сравнению с датой из имени тут сильно усложнится логика.
Скрипт придётся запускать по крону, и на границе суток могут быть ньюансы с датой, так как файлы, добавленные после 23:59:00 и до 00:00 уже не будут обработаны

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Что хоть за софт?
Может есть возможность средствами самого ПО это сделать, например поправить пару конфигов, чтобы он сам создавал каталоги и раскидывал логи? Ведь ПО умеет создавать осмысленные имена файлов логов.

Deleted
()
Последнее исправление: WiZ_Ed (всего исправлений: 1)
Ответ на: комментарий от Anon

Есть мысли насколько быстро будет это работать для десятков тысяч файлов? Each command in a pipeline is executed as a separate process (i.e., in a subshell) не наводит на мысли?

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Тут я исходил из «самого быстрого способа», как не способа реализации, а способа разобрать это дело, делая ставку на то, что у вас обыкновенная дисковая система, не рассчитанная на одновременный 16типоточный граббинг, а файлов таки по настоящему много.
Всё таки с блоками работать было бы разумно.
Ну а за логику - не 2-3 строки сверху то не существенное усложнение, в рамках данных действий, как на меня.
upd: и таки да, этот способ для первичного разгребания, если по дням. но find можно травить и на минуты; или после разгребания взять уже пофайловый разгребатель, что будет грубо по имени парсить.

Spirit_of_Stallman ★★★
()
Последнее исправление: Spirit_of_Stallman (всего исправлений: 2)
Ответ на: комментарий от zolden

Не любо — не пользуйся. Мое дело предложить, твое дело — отказаться.

Хочешь — на сях напиши. Будет быстрей.

Anon
()
Ответ на: комментарий от Deleted

Софт специфичный для конторы, варианты решить это по-хорошему на уровне конфигов я уже перебрал, так что задача стоит именно так как я описал

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Так это в первый раз сложно, потом будет проще.
Запустишь скрипт первый рах на выходные (или когда минимальная нагрузка), потом будет шустро.
А ПО кроссплатформенное? Просто это же костыли. Если оно не только под линуксом есть, то может стоит написать разработчикам о введении полезной фичи? Если реализуют, то тебе плюсанут в карму, те кто столкнулся с этим ПО.

Deleted
()

то есть разбор на следующий день не подходит

incron — по факту создания файла, запускается скрипт, который по дате создает (если надо) директорию и создает жесткую ссылку на файл в соотв. директории и удаляет файл из общей директории. Жесткие ссылки ограничивают вас одним разделом естественно.

Есть мысли насколько быстро будет это работать для десятков тысяч файлов?

P.S. ты уже у нас zsh-фил, в котором даже работа плавающей запятой есть, так что давай пили, Шура.

anonymous
()
Ответ на: комментарий от Anon

Я понимаю для тебя С роднее, чем шелл, на котором разбор строк в можно делать и без порождения процессов и вызова внешних бинарников (например echo $file[9,16] вернёт дату из имени), именно поэтому, чтобы избежать недоразумений я так детализировал описание задачи (инструмент- шелл, и самое оптимальное и быстрое решение, а не любое). Наверняка есть какой-то некостыльный вариант (возможно logrotate), про который я не в курсе

zolden ★★★★★
() автор топика
Ответ на: комментарий от anonymous

да, inсron интересный вариант, спасибо, только по поводу производительности надо будет посмотреть, а то нет уверенности насчёт вызова mv на каждый файл (предполагаю mv для пачки файлов будет оптимальнее) О, про жёсткие ссылки пропустил момент. Надо потестить в общем

zolden ★★★★★
() автор топика
Последнее исправление: zolden (всего исправлений: 1)
Ответ на: комментарий от Anon

да, до автора поделия паяльником пока не дотянуться

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Разные разделы? На одном разделе — s/mv/ln/

Есть вариант: incron — создает список файлов/директорий, а по мере его заполнения по крону пачками переносить файлы.

anonymous
()
rename 's|(app_id1_(\d{8})\d{6}_id2)|$2/$1|' *

Ещё быстрее будет переписать подстановку «*» на каком-нибудь языке, чтобы не тратить время на сортировку списка файлов, а просто читать результаты readdir в цикле.

AITap ★★★★★
()
Ответ на: комментарий от anonymous

Я вот не знаю как определяется разные ли разделы в данном контексте.
На массиве это один раздел, он монтируется по NFS но разные каталоги в разные каталоги
условно говоря так:
/storage/partition1/logdir to /home/commonlogfolder
/storage/partition1/proceedlogs to /home/proceedlogs/

zolden ★★★★★
() автор топика
Ответ на: комментарий от AITap

Только не забудь добавить, что нужен не «стандартный» rename, а хитрый!

Anon
()
Ответ на: комментарий от Anon

Да напиши ты уже на сях элементарнейший демон!
Зачем эти извращения с башем?

Нахера? У него узким местом будет копирование по сети (NFS).

anonymous
()
Ответ на: комментарий от anonymous

А, про NFS я что-то не глянул.

Но если хорошая скорость, то ничего страшного.

вот:

#define _GNU_SOURCE
#include <sys/types.h>	// opendir, closedir
#include <sys/stat.h>	// stat
#include <dirent.h>		// opendir, closedir
#include <string.h>		// strdup
#include <unistd.h>		// getcwd, chdir
#include <stdio.h>		// perror
#include <stdlib.h>		// exit, calloc
#include <errno.h>		// errno

char *app_prefix = "app_id1_";
size_t app_p_len = 8;

// process directory path, oldWD - old working directory
void proc_dir(char *path, char *oldWD){
	DIR *dir;
	struct dirent *de;
	struct stat statbuf;
	char msg[1025];
	char curname[PATH_MAX];
	char dirname[9];
	off_t size;
	if(chdir(path)){
		perror("Can't change directory");
		return;
	}
	if(!(dir = opendir(path))){
		snprintf(msg, 1024, "Couldn't open dir %s", path);
		perror(msg);
		return;
	}
	while((de = readdir(dir)) != NULL){
		if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
		if(stat(de->d_name, &statbuf)){
			snprintf(msg, 1024, "Can't STAT %s", de->d_name);
			perror(msg);
			continue;
		}
		size = statbuf.st_size;
		if(S_ISREG(statbuf.st_mode)){
			if(strncmp(de->d_name, app_prefix, app_p_len)) continue; // not our file
			strncpy(dirname, de->d_name + app_p_len, 8);
			if(mkdir(dirname, 0755) && errno != EEXIST){\
				perror("mkdir"); exit(1);
			}
			snprintf(curname, PATH_MAX-1, "%s/%s", dirname, de->d_name);
			printf("Move %s to %s\n", de->d_name, curname);
			if(link(de->d_name, curname)){
				perror("link"); exit(1);
			}
			if(unlink(de->d_name)){
				perror("unlink"); exit(1);
			}
		}
	}
	if(oldWD) chdir(oldWD);
	closedir(dir);
}

int main(int argc, char **argv){
	DIR *dir;
	char *path = NULL, *wd = NULL;
	wd = get_current_dir_name();
	if(argc == 1) path = strdup(wd);
	else{
		path = strdup(argv[1]);
		if(argc == 3){
			app_prefix = argv[2];
			app_p_len = strlen(app_prefix);
		}
	}
	if(!(dir = opendir(path))){
		perror("Couldn't open dir, try to open CWD");
		if(!(dir = opendir(wd))){
			perror("Couldn't open CWD, die");
			exit(1);
		}else{
			free(path);
			path = strdup(wd);
		}
	}
	closedir(dir);
	proc_dir(path, wd);
	free(wd); free(path);
	return 0;
}

Запускаем по крону, эта штука сканирует директорию (текущую, либо первый параметр) на предмет файлов, начинающихся на некий префикс (по умолчанию - «app_id1_», либо второй аргумент), и разбрасывает их по нужным директориям.

Anon
()
Ответ на: комментарий от zolden

например echo $file[9,16] вернёт дату из имени

Если интересно выкусывание даты из имени файла, то в баше есть конструкция типа

${parameter:offset:length}
man -P"less -p 'parameter\:offset\:length' " bash
С ней можно выкусить дату без sed и сабшеллов, но это если формат даты везде един.

Deleted
()

Самый быстрый способ это программа на Си, как вам уже сказали. Но, преждевременная оптимизация — ЗЛО. Если у вас каталоги на разных ФС, то ″mv″ будет работать медленно и всё будет упираться в его скорость работы, а не в сопоставление имён файлов.

ИМХО, для начала стоит попробовать простой bash-скрипт, который в цикле по выводу ″ls -1 -U″ парсит имена файлов с помощью встроенных в bash регулярных выражений и подстрок и на каждый файл вызывает ″mv″, и оценить скорость работы. Если скорости будет не хватать, то думать как ускорить — вызывать ″mv″ для нескольких файлов, запускать параллельно несколько ″mv″.

В целом, ИМХО, тут надо в первую очередь думать не над скоростью, а над корректностью работы скрипта в условиях неатомарности ″mv″ да ещё и по сети.

mky ★★★★★
()
Ответ на: комментарий от mky

Сказал бы что-то новое, зачем КОпитанить?

anonymous
()

Дополнение: Делать это нужно с минимальными задержками, то есть разбор на следующий день не подходит

man find, man mv

вряд-ли есть что-то быстрее.

drBatty ★★
()
Ответ на: комментарий от Anon

Хочешь — на сях напиши. Будет быстрей.

с чего ты взял? Там узкое место в mv будет(или в ln). Операции с именем будут быстрее, даже на bash+sed.

drBatty ★★
()
Ответ на: комментарий от mky

запускать параллельно несколько ″mv″.

не взлетит. Операции над каталогом атомарные, ФС одна, выполняться они будут по очереди. Про несколько файлов в mv не знаю, вряд-ли. Там вроде такой фичи не предусмотрено, давно не смотрел.

drBatty ★★
()
Ответ на: комментарий от zolden

если смонтировать по NFS всё в один каталог, то данные всё равно будут гоняться по сети?

в удалённый? будут конечно.

ЗЫЖ а зачем NFS?

drBatty ★★
()

может так попробовать?

$ ./run.sh
-0.---find-------------------------
./test.c
./test.out
./open.c
./run.sh
./test.sh
-1.---run.sh-----------------------
#!/bin/bash

echo "-0.---find-------------------------"
find -type f
echo "-1.---run.sh-----------------------"
cat run.sh
echo "-2.---test.c-----------------------"
cat test.c
echo "-3.---test.sh----------------------"
cat test.sh
echo "-4.---open.c-----------------------"
cat open.c
echo "-5.---compile test.c---------------"
gcc test.c -o test.out
echo "-6.---compile open.so--------------"
gcc -shared --PIC open.c -o open.so -ldl
echo "-7.---execute test.out-------------"
LD_PRELOAD=./open.so ./test.out
echo "-8.---execute test.sh--------------"
LD_PRELOAD=./open.so ./test.sh
echo "-9.---find-------------------------"
find -type f
-2.---test.c-----------------------
#include <stdio.h>

void createFile(const char* fname)
{
  FILE* in = fopen(fname,"wb");
  if(in) fclose(in);
}

int main(int argc, char* argv[])
{
  createFile("test.c.file");
  createFile("app_id1_YYYYMMDDHHMMSS_id3");
  return 0;
}
-3.---test.sh----------------------
#!/bin/bash

touch test.sh.file
touch "app_id1_$(date +%Y%m%d%H%M%S)_id1"
echo "test" >>"app_id1_$(date +%Y%m%d%H%M%S)_id2"
-4.---open.c-----------------------
#define _GNU_SOURCE

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <dlfcn.h>

typedef int (*type_open)(const char *pathname, int flags, ...);
typedef FILE *(*type_fopen)(const char *path, const char *mode);

static type_fopen ptr_fopen = 0;
static type_open ptr_open = 0;
void __attribute__((constructor)) my_init(void)
{
  ptr_fopen = dlsym(RTLD_NEXT, "fopen");
  ptr_open = dlsym(RTLD_NEXT, "open");
}

#define NEEDLE "app_id1_"
char* testPath(const char *path)
{
  char *ptr = strstr(path, NEEDLE);
  if(ptr!=0)
  {
    /// 14 - strlen("YYYYMMDDHHMMSS")
    int index,pos,len = strlen(path);
    char *buf = (char*)calloc(len+14+1,1);
    index = ptr-path;
    memcpy(buf, path, pos=index);
    memcpy(&buf[pos], &ptr[strlen(NEEDLE)], 14);
    pos+=14;
    mkdir(buf, 0777);
    buf[pos++] = '/';
    index += strlen(NEEDLE);
    memcpy(&buf[pos], &path[index], len-index);
    return buf;
  }
  return 0;
}

FILE* fopen(const char *path, const char *mode)
{
  char*tmp = testPath(path);
  if(tmp)
  {
    FILE* res = ptr_fopen(tmp, mode);
    free(tmp);
    return res;
  }
  return ptr_fopen(path, mode);
}

int open(const char *pathname, int flags, ...)
{
  mode_t mode = 0;
  va_list ap;
  va_start(ap, flags);
  mode = (mode_t)va_arg(ap, mode_t);
  va_end(ap);

  return open_mode(pathname, flags, mode);
}

int open_mode(const char *pathname, int flags, mode_t mode)
{
  char*tmp = testPath(pathname);
  if(tmp)
  {
    int res = ptr_open(tmp, flags, mode);
    free(tmp);
    return res;
  }
  return ptr_open(pathname, flags, mode);
}
-5.---compile test.c---------------
-6.---compile open.so--------------
-7.---execute test.out-------------
-8.---execute test.sh--------------
-9.---find-------------------------
./YYYYMMDDHHMMSS/YYYYMMDDHHMMSS_id3
./20130430205925/20130430205925_id2
./20130430205925/20130430205925_id1
./test.c
./test.out
./test.c.file
./open.so
./open.c
./run.sh
./test.sh
./test.sh.file

anonymous
()
Ответ на: комментарий от drBatty

ЗЫЖ а зачем NFS?

Такова уж архитектура, я её не могу менять.
Массив на самом деле достаточно хороший, есть даже 10G порты для подключения по оптике, так что утверждать, что все задержки порождаются NFS я бы не стал.

zolden ★★★★★
() автор топика
Ответ на: комментарий от drBatty

Операции над каталогом атомарные, ФС одна

Сначала в задании было несколько точек монтирования по NFS, mv бы работал как ″cp; rm″, в последнем абзаце своего поста я про неатомарность и писал.

В mv (во всяком случае GNU) есть опция ″-t″, в этом случае он сваливает все указанные ему файлы в один каталог. То есть если сначала взять список файлов и определить группу файлов, которые нужно помещать в один каталог, то перемещение этой группы можно сделать одной командой ″mv″ и сэкономить на fork/exec.

mky ★★★★★
()
Ответ на: комментарий от zolden

Если перемещение файла будет внутри одной точки монтирования, то по сети данные передаваться не будут, NFS умеет rename(). Единственное, что man'е есть оговорка, что неудачное выполнение rename() в случае NFS не гарантирует, что файл не был переименован.

Не знаю, зачем вам анонимный сишник так настойчиво советует жёсткие ссылки, перемещение файла это системный вызов rename().

mky ★★★★★
()
Ответ на: комментарий от anonymous
diff open.c.old open.c
30c30
<     char *buf = (char*)calloc(len+14+1,1);
---
>     char *buf = (char*)calloc(len+14+1+1,1);
37d36
<     index += strlen(NEEDLE);
anonymous
()
Ответ на: комментарий от zolden

говорят lua быстро работает, его именно для таких задач изобретали

anonymous
()
Ответ на: комментарий от zolden

Такова уж архитектура, я её не могу менять.

в след раз пиши в первом посте, а то непонятно.

drBatty ★★
()
Ответ на: комментарий от mky

В mv (во всяком случае GNU) есть опция ″-t″, в этом случае он сваливает все указанные ему файлы в один каталог. То есть если сначала взять список файлов и определить группу файлов, которые нужно помещать в один каталог, то перемещение этой группы можно сделать одной командой ″mv″ и сэкономить на fork/exec.

fork это мелочь, на самом деле. Работа с каталогами отъедает ОЧЕНЬ много времени. Потому я не думаю, что форки сильно что-то ускорят. Хотя конечно ТСу виднее. Может у него особая mv, которая умеет все файлы пихать в одну транзакцию.

drBatty ★★
()
Ответ на: комментарий от mky

Не знаю, зачем вам анонимный сишник так настойчиво советует жёсткие ссылки, перемещение файла это системный вызов rename().

AFAIK rename(2) сама создаёт хардлинки, если сможет (но это в пределах одной обычной ФС).

drBatty ★★
()
Ответ на: комментарий от drBatty

Если не лень, то вот такой тест:

$ mkdir LOR-test; cd LOR-test

$ time for ((i=1; i<30000; i++)) ; do > $i ; done

real    0m1.583s
user    0m0.427s
sys     0m1.083s

$ mkdir A
$ time for ((i=1; i<30000; i++)) ; do mv $i A/$i ; done

real    0m25.106s
user    0m5.743s
sys     0m19.182s

$ time for ((i=1; i<30000; i++)) ; do > $i ; done

real    0m1.511s
user    0m0.448s
sys     0m1.005s

$ mkdir B
$ time mv -t B [0-9]*

real    0m0.550s
user    0m0.134s
sys     0m0.410s

25.06/0.55 = 45.6 раз

Процессор i7 930 @ 2.80GHz, ext3, 3ware RAID 1 на двух ST3146356SS (15k rpm). На P-4 2,8 ГГц с SATA 300 Гб винтом разница ещё больше — 50,5 раз.

Что будет в случае ТС, у которого NFS по 10 Гбит оптике сказать не могу, тестировать не на чем.

Если считате тест не корректным, предложите свой.

mky ★★★★★
()
Ответ на: комментарий от mky

Если считате тест не корректным, предложите свой.

корректный. Да, тут bash тормозной, я, кстати, предлагал find, там можно кучу файлов втиснуть в одну mv -t target/

Кстати, вам -t не нужна. Можно

mv [0-9]* B
(если B — каталог). А вот для случая с
find -name "[0-9]*" -exec mv -t B {} +
думаю будет тоже самое. (я предлагал именно это, но тут ещё дальше надо писать, а мне лениво).

drBatty ★★
()

А если например наколбасить на сях с применением inotify'я демона, который будет пересовывать файл куда надо сразу после того, как словил про него IN_CLOSE_WRITE?
Ну или вообще превентивно создавать pipe с предсказанным именем, сливающий содержимое в одноименный файл в нужном каталоге. Вот это было бы знатное извращение.

thesis ★★★★★
()
Ответ на: комментарий от drBatty

Это не bash тормозной, это fork()+exec(), причём, как показали здесь Почем fork для народа? , основные затраты на exec(), а не на fork(), но для sh-скриптов fork() без exec() особого смысла не имеет.

Вариант с find вполне годный, возможно туда нужно ещё xargs добавить, а то ТС пугает очень большим количеством файлов, но это уже его забота.

mky ★★★★★
()
Ответ на: комментарий от mky

Вариант с find вполне годный, возможно туда нужно ещё xargs добавить

я УЖЕ добавил. Читай про '+' в команде find.

Это не bash тормозной, это fork()+exec()

в данном случае расходы будут не на fork+exec. А баш тоже тормозной сам по себе. Особенно с лишними fork+exec.

а то ТС пугает очень большим количеством файлов, но это уже его забота.

в этом-то и проблема. Расходы на переписывание каталогов будут НАМНОГО больше bash и exec. Если файлов действительно МНОГО.

drBatty ★★
()

Спасибо всем ( WiZ_Ed, Spirit_of_Stallman, Anon, fargred, mky, drBatty и anonymous) за советы и разнообразие вариантов, как я понимаю первоочередная оптимизация в лоб - это монтирование массива в один каталог для уменьшения накладных расходов по NFS. Остальные вещи потестирую после праздников

zolden ★★★★★
() автор топика
Ответ на: комментарий от drBatty

'+' в команде ″find″ даёт меньше возможностей, чем ″xargs″, так как можно сказать ″xargs -P″.

По мне, параллельное выполнение ″mv″ в случае с NFS должно давать выигрыш. NFS клиент отправляет запрос на rename() файла и ждёт, пока сервер его обработает. При это заметное время уходит на передачу запроса/ответа по сети. Если одновременно будет работать несколько ″mv″, то файловая система сервера будет выполнять их запросы последовательно, но rename() от одного клиента будет выполнятся во время предачи запроса по сети от другого клиента.

mky ★★★★★
()
Ответ на: комментарий от mky

По мне, параллельное выполнение ″mv″ в случае с NFS должно давать выигрыш.

если честно — не знаю. Пусть ТС проверяет, интересно будет посмотреть на результат.

drBatty ★★
()
Ответ на: комментарий от drBatty

Операции с именем будут быстрее, даже на bash+sed

А запуск/убийство процессов?

Таки одним процессом будет быстрей.

Anon
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.