1 (изменено: Progressive, 2010-09-22 16:47:09)

Тема: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Доброго времени суток.
Необходимо:
VBS обработчик текстовых логов, который умеет вставлять полученные данные, из лог-файлов, в Oracle.

Имеется:
- лог-файлы такого типа: "ex10091011.log" (создан сервером 10.09.2010, с чего следует, что в имени файла сначало стоит год, месяц, число, и "11" - время, когда был создан лог в часах), которые находятся в определенной папке (к примеру С:\Logs\)
- в каждом из этих файлов первые 4 строки считывать ненужно (Там находится информация за знаком "#" о том, что это за лог, и кто его создал, и какие поля он в себе содержит через разделитель пробел)

Вот и само содержание лог-файла:

#Software: Microsoft Internet Information Services 6.0
#Version: 1.0
#Date: 2010-09-10 11:04:29
#Fields: date time cs-method cs-uri-stem cs-uri-query s-port c-ip cs-host sc-status sc-substatus sc-bytes cs-bytes time-taken 
2010-09-10 11:04:29 GET /index.html - 8080 127.0.0.1 test.com.ua:8080 200 0 3902 724 312
2010-09-10 11:04:46 GET /person.html - 8080 127.0.0.1 test.com.ua:8080 404 0 2102 694 0

- в Oracle создана таблица (STATS_SITE) с такими же полями что и в лог-файле:
| DATA | TIME | CS_METHOD | CS_URI_STEM | CS_URI_QUERY | S_PORT | C_IP | CS_HOST | SC_STATUS | SC_SUBSTATUS | SC_BYTES | CS_BYTES | TIME_TAKEN |
(Разделитель "|" вставлен для удобства чтения)


Постановка задачи:
- считать каждое поле с каждой строки лог-файла(2010-09-20, 11:04:29, GET, /index.html, 8080, 127.0.0.1, test.com.ua:8080, 200, 0, 3902, 724, 312) и добавить в таблицу оракла STATS_SITE по соответственным полям.

Например:
- в строке №5 первое поле, до первого пробела, имеет вид 2010-09-10, его нужно считать с лога и добавить в Oracle таблицу STATS_SITE в ячейку DATA, ну и так дальше...


Мои размышления.
   Часть 1. Первое что лучше сделать - скопировать из папки, куда генерируются лог-файлы сервером, все логи, к примеру за сутки, в новую папку - "Temp"(в этой папке будут содержатся те лог-файлы которые были созданы в период между последними запусками скрипта), если скопировать один из логов не удалость, то и фиг с ним пусть остается, поскольку планировщик позже запустит скрипт снова и занятый процессом лог-файл уже будет свободен. Дальше очистить корневую папку (С:\Logs\ - это делается для очистки диска на котором работает сервер, ибо с практики этих лог-файлов потом создается на сотни мегабайт, что очень плохо), потом создать еще одну папку "AllLogs" - в ней будут хранится логи за период одного месяца и каждые 30 дней очищятся, мало ли, а вдруг сервер оракла упадет.
   Эту часть я решил. Скрипт создан и чудесно работает.

   Часть 2. Процес считывания лог-файла. Теоретически понятно, что нужно использовать FSO, но как описать нужные мне поля?Создать временный файл и записать построчно все поля, разделенные в каждой строке пробелом, что нужны мне и сделать запрос в базу, но как это сделать максимально безболезненно?
    Если позволите я выложу где я "хожу" вокруг да около, а вы если не сложно подскажите практическими примерами что да как...

Dim FSO, file, fileName
fileName = "С:\Temp\ex10091011.log"

Set FSO = CreateObject("Scripting.FileSystemObject")
if Not FSO.FileExists(fileName) Then
    Set file = FSO.CreateTextFile(fileName, true)
    file.Close
End if

Set file = FSO.GetFile(fileName)

Dim text
Set text = FSO.OpenTextFile(fileName, 1, false)

Dim stringArray, lastString, idx
stringArray = Split(text.ReadAll, Chr(10), -1, 1)

text.Close

idx = UBound(stringArray)
Do Until idx < 0
  lastString = stringArray(idx)
  if Len(lastString) > 0 Then
    Dim data, time, cs-method, cs-uri-stem, cs-uri-query, s-port, c-ip, cs-host, sc-status, sc-substatus, sc-bytes, cs-bytes, time-taken, parsing, count
    parsing = Split(lastString, " ")
    count = UBound(parsing)
      
    if (count > 13) Then
      data = parsing(0)
      time = parsing(1)
      cs-method = parsing(2)
      cs-uri-stem = parsing(3)
      cs-uri-query = parsing(4)
      s-port = parsing(5)
      c-ip = parsing(6)
      cs-host = parsing(7)
      sc-status = parsing(8)
      sc-substatus = parsing(9)
      sc-bytes = parsing(10)
      cs-bytes = parsing(11)
      time-taken = parsing(12)
        
' ??????????????????????
' ?????????????????????? по идее цикл если i>13 then ??? то что? 
      
    End If
  End If
  
  idx = idx - 1
Loop


'Конектимся к базе и отправляем полученные данные в таблицу

Set OraSession = CreateObject("OracleInProcServer.XOraSession")
Set OraDatabase = OraSession.OpenDatabase("oracle.test", "testUser/testPass", 0)
OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_NUMERIC_CHARACTERS= '.`'")
OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy hh24:mi:ss'")
OraDatabase.ExecuteSQL("INSERT INTO STATS_SITE VALUES ( " & data & ", " & time & ",  " & cs-method & ", " & cs-uri-stem & ", " & cs-uri-query & ", " & s-port & ", " & c-ip & ", " & cs-host & "," & sc-status & "," & sc-substatus & ", " & sc-bytes & ", " & cs-bytes & ", " & time-taken & ")")

Код с ошибками, "возможно" специальными... Это сделанно с целью отсеивания ответов вида: "да зайди в гугл там все есть", "учите матчасть" и т.д. и т.п.
Мне нужны практические примеры!

З.Ы. Человек который дельно поможет не останется без материального "Спасибо"!

2 (изменено: Rumata, 2010-09-22 21:57:22)

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Я бы сделал просто - читать входной поток построчно и выпуливать в выходной поток готовые sql-запросы:

cscript log2sql.vbs < C:\Logs\ex10091011.log > C:\Temp\ex10091011.sql

А если оракловая консоль поддерживает конвейеры то и так

cscript log2sql.vbs < C:\Logs\ex10091011.log | plsql опции соединения с БД

Вот только язык поменял бы VBscript на JScript. У него более гибкие стредства работы со строками.

( 2 * b ) || ! ( 2 * b )

3

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Я бы тоже поменял язык Но увы другого пока не дано...
Спасибо за вариант решения задачи, но увы не подходит...

Может кто-то хотябы знает как написать в VBS-е правильно OraDatabase.ExecuteSQL("INSERT INTO ... ")
Что после инсёрт инто? Так же как и в обычном запросе по логике, но я буду вставлять не численные параметры а переменные которые содержат в себе параметры...
Что-то я совсем запутался...

4

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

все также как и во всех sql-запросах http://aam.ugpl.de/?q=ru/node/815

INSERT INTO ТАБЛИЦА VALUES ('строка', 'дата', число)
( 2 * b ) || ! ( 2 * b )

5 (изменено: Progressive, 2010-09-23 13:34:18)

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Ну вот после некоторых махинаций еще один вариант решения задачи №2:

Dim FSO, file, fileName
fileName = "С:\Temp\ex10091011.log"

Set FSO = CreateObject("Scripting.FileSystemObject")
if Not FSO.FileExists(fileName) Then
    Set file = FSO.CreateTextFile(fileName, true)
    file.Close
End if

Set file = FSO.GetFile(fileName)

Dim text
Set text = FSO.OpenTextFile(fileName, 1, false)

Dim stringArray, lastString, idx
stringArray = Split(text.ReadAll, Chr(10), -1, 1)

text.Close

idx = UBound(stringArray)
Do Until idx < 0
  lastString = stringArray(idx)
  if Len(lastString) > 0 Then
    Dim data, time, cs_method, cs_uri_query, s_port, c_ip, cs_host, sc_status, sc_substatus, sc_bytes, cs_bytes, time_taken, parsing, count
    parsing = Split(lastString, " ")
    count = UBound(parsing)
      
    if (count > 13) Then
      data = parsing(0)
      time = parsing(1)
      cs_method = parsing(2)
      cs_uri_query = parsing(3)
      s_port = parsing(5)
      c_ip = parsing(6)
      cs_host = parsing(7)
      sc_status = parsing(8)
      sc_substatus = parsing(9)
      sc_bytes = parsing(10)
      cs_bytes = parsing(11)
      time_taken = parsing(12)
      
    End If
  End If
  
  idx = idx - 1
Loop

'insert to oracle
Set OraSession = CreateObject("OracleInProcServer.XOraSession")
Set OraDatabase = OraSession.OpenDatabase("oracle.test", "testUser/testPass", 0)
OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_NUMERIC_CHARACTERS= '.`'")
OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy hh24:mi:ss'")
OraDatabase.ExecuteSQL("INSERT INTO STATS_SITE VALUES ( '" & data & "', '" & time & "',  '" & cs_method & "', '" & cs_uri_query & "', '" & s_port & "', '" & c_ip & "', '" & cs_host & "', '" & sc_status & "', '" & sc_substatus & "', '" & sc_bytes & "', '" & cs_bytes & "', '" & time_taken & "')")

Осталось решить 2 проблемы:
1. Как исключить считывание данных с первых 4-ох строк (Интуитивно понятно что нужно использовать SkipLine, но как его использовать в этом коде? )
2. Как правильно отправить запрос в базу Oracle, после каждого считывания строки.

Объясню на пальцах пункт №2.
После первой обработки самой последней строки получаю такие данные:

      data = 2010-09-10
      time = 11:04:46
      cs_method = GET
      cs_uri_query = /person.html
      s_port = 8080
      c_ip = 127.0.0.1
      cs_host = test.com.ua:8080
      sc_status = 404
      sc_substatus = 0
      sc_bytes = 2102
      cs_bytes = 694
      time_taken = 0

Эти данные должны добавится в БД

После обработки цикла второй рас уже передпоследней строки получаем такие данные:

      data = 2010-09-10
      time = 11:04:29
      cs_method = GET
      cs_uri_query = /index.html
      s_port = 8080
      c_ip = 127.0.0.1
      cs_host = test.com.ua:8080
      sc_status = 200
      sc_substatus = 0
      sc_bytes = 3902
      cs_bytes = 724
      time_taken = 312

Опять таки эти данные должны добавится в БД
ну и так дальше...

Вот вопрос - как это реализовать то?

6 (изменено: Progressive, 2010-09-23 16:24:39)

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Ура
Решил все проблемы кроме толкового СкипЛайна.

Вот кушайте полностью рабочий скрипт:

Dim FSO, file, fileName
fileName = "C:\Temp\ex10091011.log"

Set FSO = CreateObject("Scripting.FileSystemObject")
if Not FSO.FileExists(fileName) Then
    Set file = FSO.CreateTextFile(fileName, true)
    file.Close
End if

Set file = FSO.GetFile(fileName)

Dim text
Set text = FSO.OpenTextFile(fileName, 1, false)

Dim stringArray, lastString, idx
text.SkipLine
text.SkipLine
text.SkipLine
text.SkipLine
stringArray = Split(text.ReadAll, Chr(13), -1, 1)

text.Close

idx = UBound(stringArray)
Do Until idx < 0
  lastString = stringArray(idx)
  if Len(lastString) > 0 Then
    Dim data, time, cs_method, cs_uri_query, s_port, c_ip, cs_host, sc_status, sc_substatus, sc_bytes, cs_bytes, time_taken, parsing, count
    parsing = Split(lastString, " ")
    count = UBound(parsing)
      
    if (count > 5) Then
      data = parsing(0)
      time = parsing(1)
      cs_method = parsing(2)
      cs_uri_query = parsing(3)
      s_port = parsing(5)
      c_ip = parsing(6)
      cs_host = parsing(7)
      sc_status = parsing(8)
      sc_substatus = parsing(9)
      sc_bytes = parsing(10)
      cs_bytes = parsing(11)
      time_taken = parsing(12)

      Set OraSession = CreateObject("OracleInProcServer.XOraSession")
      Set OraDatabase = OraSession.OpenDatabase("oracle.test", "testUser/testPass", 0)
      OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_NUMERIC_CHARACTERS= '.`'")
      sql = "INSERT INTO STATS_SITE VALUES ( to_date('" & data & " " & time & "', 'yyyy-mm-dd hh24:mi:ss'),'" & cs_method & "', '" & cs_uri_query & "', '" & s_port & "', '" & c_ip & "', '" & cs_host & "', '" & sc_status & "', '" & sc_substatus & "', '" & sc_bytes & "', '" & cs_bytes & "', '" & time_taken & "')"
      OraDatabase.ExecuteSQL(sql)
    End If

  End If
  idx = idx - 1
Loop

7

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Progressive пишет:

Решил все проблемы кроме толкового СкипЛайна.

А чем не толковый?

8

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

alexii пишет:
Progressive пишет:

Решил все проблемы кроме толкового СкипЛайна.

А чем не толковый?

А вдруг поставится IIS другой версии, а там закоментированных строк, что за знаком # будет всего 3? или наоборот 5?

9

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

А, Вы в этом смысле… теперь ясно.

10

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Кстати, я и не думал, что .ReadAll читает с позиции указателя. Полагал, что только целиком, весь файл с начала. Спасибо.

11 (изменено: Rumata, 2010-09-23 21:09:26)

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

while ( ! stream.AtEndOfStream ) {
    var line = stream.ReadLine();
    if ( line.charAt(0) == '#' ) {
        // Пропустить строку, начинающуюся с символа #
        continue;
    }
    // Работаем со строкой
}

Простите, что на JScript. Просто на нем все время "думаю". Но для иллюстрации алгоритма это не принципиально. Если что-то непонятно, готов перевести на VBScript.

( 2 * b ) || ! ( 2 * b )

12

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Наверное, можно оставить и .ReadAll(), наподобие:

…
With objFSO.OpenTextFile("…")
    Do Until .AtEndOfStream
        strLine = .ReadLine
        
        If Left(Trim(strLine)) <> "#" Then
            strArray = Split(strLine & vbCr & .ReadAll, vbCr) ' А тут в файле точно только «0x0D», а не «0x0D,0x0A»?
            
            .Close
            
            Exit Do
        End If
    Loop
    
    .Close
End With
…

13

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

alexii пишет:

Кстати, я и не думал, что .ReadAll читает с позиции указателя. Полагал, что только целиком, весь файл с начала. Спасибо.

На здаровье. Обращяйтесь если что

Кстати интересный вариант с Left! Но мне кажется рид алл подходит для небольших файлов, лучше все таки обрабатывать по строчно. Завтра попробую и так и так, посмотрим что быстрее окажется

2 Rumata
Спс, я принцип то понимал... ну попробуем завтра практически, если не получится то обращусь

Кстати, а никто не подскажет как обрабатывать все лог-файлы которые находятся в папке по очереди, там по дате создания например или другому атрибуту?
К примеру есть папка Temp а в ней штук 30 файлов разришения *.log и во всех одинаковая структура, само собой!

Вот названия к примеру:

ex10092008.log
ex10092009.log
ex10092010.log
ex10092011.log
ex10092012.log
ex10092013.log
ex10092014.log
ex10092015.log
...
ex10092023.log
ex10092100.log
ex10092101.log
ex10092102.log
ex10092103.log

Вот такой вид будет файлов в папке. И вот как бы каждый обработать этим скриптом что я написал Не будешь жеш менять ручьками название лог-файла в скрипте каждый рас...
Кто что думает по этому поводу?

14

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Как обычно, FSO->.GetFolder(…)->Files, второй пример из последней ссылки позволяет перебрать все файлы в папке.

15

Re: VBS: Обработчик текстовых логов, вставка полученных данных в Oracle

Ну что ж друзья держите еще некоторые доработки.
- добавил логирование работы самого скрипта с перезаписю лога при каждом новом запуске
- поменял структуру скрипта, для удобства чтения

ВНИМАНИЕ! Вопросы:
№1 - как правильней написать функцию удаления обработанных файлов, при условии что запрос в БД прошел успешно
№2 - как исключить из считывания лог-файла целую строку что начинается на знак "#", пробовал и Left'ом , но при этом ругается на UBound(неправильный тип переменной stringArray).

Сам код: (он рабочий, но вот нужно усовершенствовать)

del_vbslog = True
files_move = True
log_reader = True
Dim SourceFolder, pathTo, idx, file, FSO, FileLog
SourceFolder = "D:\Temp&logs\WebLogFiles\W3SVC1"
pathTo = SourceFolder + "\Export_To_Oracle"
Set FSO = CreateObject("Scripting.FileSystemObject")
FileLog = Left(WScript.ScriptName,(Len(WScript.ScriptName)-4)) & "_" & DatePart("yyyy",Date) & "_" & DatePart("m",Date) & "_" & DatePart("d",Date) & "." & "log"

Main()
'-----------------------------------------------------------
Sub Main()
  If del_vbslog Then DelVbsLog()
  If files_move Then LogFilesMove()
  If log_reader Then LogReader()
End Sub

'-----------------------------------------------------------
'Удаляем лог-файл нашего скрипта, если таковой существует
Sub DelVbsLog()
  If FSO.FileExists(FileLog) Then
    Set File = FSO.GetFile(FileLog)
    Const DELETE_FILE_FORCE = True
    File.Delete DELETE_FILE_FORCE
  End If
End Sub
'-----------------------------------------------------------

'-----------------------------------------------------------
'Перемещаем лог-файлы в папку Export_To_Oracle
Sub LogFilesMove()

If Not FSO.FolderExists(pathTo) Then
    FSO.CreateFolder pathTo
End If
On Error Resume Next
WriteLog("Перемещены\скопированы следующие файлы: " & vbCrLf)
For Each File In FSO.GetFolder(SourceFolder).Files
    path_to_file=pathTo & "\" & File.Name
    if FSO.FileExists(path_to_file) then FSO.DeleteFile(path_to_file)
    FSO.MoveFile File.Path, path_to_file
    If Err.number <> 70 Then Log_Ok_Or_ErrorAndQuit 'исключаем ошибку если файл занят другим процессом
    WriteLog(path_to_file)
Next
End Sub

Sub Log_Ok_Or_ErrorAndQuit
    If Err.number <> 0 Then
        WriteLog "Error #" + Err.number + " :" + Err.Description, 1
        WScript.Quit
    End if
End Sub

'-----------------------------------------------------------
'Обрабатываем каждый лог-файл и производим запись в БД
Sub LogReader()

Dim TheFolder, TheFiles, fileName
Set TheFolder = FSO.GetFolder(pathTo)
Set TheFiles = TheFolder.Files

Set OraSession = CreateObject("OracleInProcServer.XOraSession")
Set OraDatabase = OraSession.OpenDatabase("oracle.test", "root/password", 0)
OraDatabase.ExecuteSQL("ALTER SESSION SET NLS_NUMERIC_CHARACTERS= '.`'")

For Each fileName In TheFiles

  if Not FSO.FileExists(fileName) Then
      Set file = FSO.CreateTextFile(fileName, true)
      file.Close
  End if

  Set file = FSO.GetFile(fileName)

  Dim text
  Set text = FSO.OpenTextFile(fileName, 1, false)

  WriteLog(vbCrLf & "Был обработан файл: " & vbCrLf & fileName)
  
  Dim stringArray, lastString
  text.SkipLine
  text.SkipLine
  text.SkipLine
  text.SkipLine
  stringArray = Split(text.ReadAll, Chr(13), -1, 1)
  text.Close

    WriteLog(vbCrLf & "Выполнены следующие действия: ")
  idx = UBound(stringArray)
  isRunCommit = false
  Do Until idx < 0
    lastString = stringArray(idx)
    if Len(lastString) > 0 Then
      Dim data, time, cs_method, cs_uri_stem, cs_uri_query, s_port, c_ip, sc_status, sc_substatus, sc_bytes, cs_bytes, time_taken, parsing, count
      parsing = Split(lastString, " ")
      count = UBound(parsing)
      
      if (count > 5) Then
        data = parsing(0)
        time = parsing(1)
        cs_method = parsing(2)
        cs_uri_stem = parsing(3)
        cs_uri_query = parsing(4)
        s_port = parsing(5)
        c_ip = parsing(6)
        sc_status = parsing(7)
        sc_substatus = parsing(8)
        sc_bytes = parsing(9)
        cs_bytes = parsing(10)
        time_taken = parsing(11)

        if cs_uri_query = "-" Then 'если вместо данных прочерк, то заменяем на пустоту. У вас может такого не быть, либо встречатся другой символ
          cs_uri_query = ""
        End if

        sql = "INSERT INTO STATS_SITE VALUES ( to_date('" & data & " " & time & "', 'yyyy-mm-dd hh24:mi:ss'), '" & cs_method & "', '" & cs_uri_stem & "', '" & cs_uri_query & "', '" & s_port & "', '" & c_ip & "', '" & sc_status & "', '" & sc_substatus & "', '" & sc_bytes & "', '" & cs_bytes & "', '" & time_taken & "')"
        OraDatabase.ExecuteSQL(sql)
        isRunCommit = true
        WriteLog(vbCrLf & sql)
      End If
    End If
    idx = idx - 1
  Loop
  if isRunCommit = True Then
  'file.Delete 'как бы удаление файла после удачной обработки и добавления в базу
  'OraSession.CommitTrans
  end if
  'if isRunCommit = True Then OraSession.CommitTrans
Next
End Sub

'-----------------------------------------------------------
'Функция записи лог файла

Function WriteLog(Text)
   Const ForReading = 1, ForWriting = 2
   Dim f
   
  Set f = FSO.OpenTextFile(FileLog, 8, True)    
    f.Write Text & vbCrLf
    f.Close
   
End Function