# Analysis of Log Files with PowerShell

PowerShell là công cụ rất hữu ích để phân tích tệp nhật ký. Bạn có thể đọc, tìm kiếm và thao tác với tệp nhật ký bằng các cmdlet như `Get-Content`, `Select-String`, `ConvertFrom-Json` và `ConvertTo-Json`.

Trong phần tiếp theo của khóa học, chúng ta sẽ làm việc với tệp nhật ký của máy chủ web IIS. Hãy đọc nội dung của tệp này:

{% code overflow="wrap" %}

```powershell
Get-Content -Path "C:\inetpub\logs\LogFiles\W3SVC1\u_ex230629.log"
```

{% endcode %}

Khi chạy cmdlet này, nó sẽ đọc tệp nhật ký trong đường dẫn mà ta cung cấp làm tham số và hiển thị nội dung của nó trên màn hình.

<figure><img src="/files/siLTN8zTMhswkt2IKfPJ" alt=""><figcaption></figcaption></figure>

Nếu mục tiêu của chúng ta là liệt kê các dòng chứa cụm từ “letsdefend” thay vì toàn bộ tệp, thì có một cách đơn giản để làm điều này:

{% code overflow="wrap" %}

```powershell
Get-Content -Path "C:\inetpub\logs\LogFiles\W3SVC1\u_ex230629.log" | Select-String -Pattern "letsdefend"
```

{% endcode %}

Khi chạy lệnh này, nó sẽ chỉ hiển thị các dòng chứa cụm từ “letsdefend” trong toàn bộ tệp nhật ký:

<figure><img src="/files/EN2pXMACmp4fMLfqtVr9" alt=""><figcaption></figcaption></figure>

Khi muốn theo dõi thay đổi của tệp này trong thời gian thực, chúng ta có thể sử dụng tham số `-Wait`. Khi thực thi lệnh dưới đây, cmdlet `Get-Content` sẽ hiển thị nội dung hiện tại của tệp lên màn hình và sau đó chuyển sang chế độ chờ thay vì quay lại dòng lệnh. Nếu nội dung mới được thêm vào tệp (trong ví dụ này, nếu có yêu cầu mới đến máy chủ web mà chúng ta đang theo dõi nhật ký), nó sẽ bổ sung nội dung mới này lên màn hình.

{% code overflow="wrap" %}

```powershell
Get-Content -Path "C:\inetpub\logs\LogFiles\W3SVC1\u_ex230629.log" -Wait | Select-String -Pattern "letsdefend"
```

{% endcode %}

Khi kết hợp tính năng này với ví dụ `Select-String` trước đó, chúng ta sẽ có thể thấy ngay khi một dòng chứa mẫu chúng ta đang tìm kiếm được ghi vào tệp nhật ký mà chúng ta theo dõi.

Trong ảnh chụp màn hình, khi chúng ta chạy lệnh, nó hiển thị các dòng chứa "letsdefend" trong tệp nhật ký liên quan và sau đó bắt đầu giám sát tệp nhật ký. Khi gửi các tham số “`waitExample=letsdefend`” đến tệp index của máy chủ web, dòng mới sẽ được thêm vào.

<figure><img src="/files/FrIXYDnWptiXRI2qsxe9" alt=""><figcaption></figcaption></figure>

Hãy viết một script trên cùng tệp nhật ký để đếm số lượng yêu cầu từ cùng một địa chỉ IP:

{% code overflow="wrap" %}

```powershell
# Đường dẫn đến tệp nhật ký
$filePath = "C:\inetpub\logs\LogFiles\W3SVC1\u_ex230629.log"

# Lấy chỉ số của trường 'c-ip' (IP khách hàng) từ tiêu đề tệp nhật ký
$ipFieldIndex = (Get-Content -Path $filePath | Where-Object { $_ -match "^#Fields:" }).Split(' ').IndexOf('c-ip') - 1

# Bỏ qua các dòng thông tin
$logContent = Get-Content -Path $filePath | Where-Object { $_ -notmatch "^#" }

$ipCount = @{}

foreach($line in $logContent) {
	$ip = $line.Split(' ')[$ipFieldIndex]
	if ($ipCount.ContainsKey($ip)) {
		$ipCount[$ip]++
	} else {
		$ipCount[$ip] = 1
	}
}

$ipCount
```

{% endcode %}

Trong script này, chúng ta tìm vị trí của cột `c-ip` bằng cách đọc dòng "Fields" có trong tệp nhật ký IIS tiêu chuẩn. Nhờ đó, chúng ta biết cột nào trong các dòng nhật ký cần đọc.

<figure><img src="/files/Pw1F4HjgLW6asi0YXVT5" alt=""><figcaption></figcaption></figure>

Chúng ta tạo một hashtable (`$ipCount`) và thêm mỗi địa chỉ IP khi thấy lần đầu tiên vào hashtable này. Nếu một địa chỉ IP đã tồn tại trong hashtable, chúng ta tăng số đếm của nó lên một. Nếu chưa tồn tại, chúng ta tạo một cặp key-value mới và đặt số đếm của nó là 1. Khi script hoàn thành, chúng ta in ra hashtable `$ipCount` để hiển thị số lượng yêu cầu từ mỗi địa chỉ IP.

Cuối cùng, hãy xem đầu ra của chúng ta.

<figure><img src="/files/znD0q6AHcNyesOQRJW0W" alt=""><figcaption></figcaption></figure>

Bằng cách thay đổi các trường trong dòng `#Fields`, chúng ta có thể trích xuất số lượng của chúng trong URL yêu cầu thay vì địa chỉ IP nguồn, ví dụ:

<figure><img src="/files/Wo71qaxPkptm0CKo2ucY" alt=""><figcaption></figcaption></figure>

Bây giờ, hãy làm việc với tệp nhật ký DHCP lần này và sử dụng cấu trúc đối tượng PowerShell trong ví dụ này:&#x20;

Chúng ta có một tệp nhật ký như dưới đây. Tệp nhật ký này thường được tìm thấy trên các máy chủ DC có vai trò máy chủ DHCP được cài đặt và ghi lại các hoạt động của DHCP.

<figure><img src="/files/0lyY3c6Uhu6U3y3Nr0hv" alt=""><figcaption></figcaption></figure>

Mục đích của đoạn script dưới đây là để kiểm tra tệp nhật ký này và ghi lại các hoạt động của địa chỉ MAC mà chúng tôi đã cung cấp:

{% code overflow="wrap" %}

```powershell
$macAddress = "00-10-22-01-23-45"
$logPath = "dhcp.log"

$events = Get-Content -Path $logPath |
Where-Object { $_ -match $macAddress } |
ForEach-Object {
	$fields = $_.Split(',')
	$eventDate = [DateTime]::ParseExact($fields[1], "MM/dd/yy HH:mm:ss", $null)
	$eventType = $fields[2]
	$ipAddress = $fields[3]
	$deviceName = $fields[4]
	$mac = $fields[5]

	if ($mac -eq $macAddress) {
		New-Object -TypeName PSObject -Property @{
			EventDate = $eventDate
			EventType = $eventType
			IPAddress = $ipAddress
			DeviceName = $deviceName
			MACAddress = $mac
			}
		}
}

$events | Sort-Object -Property EventDate
```

{% endcode %}

Giải thích chi tiết hơn:

```powershell
$macAddress = "00-14-22-01-23-45"
```

Trong dòng trên, chúng ta cung cấp địa chỉ mac mà chúng ta sẽ tìm kiếm trong tệp nhập ký dưới dạng giá trị của biến `$macAddress`

```powershell
$logPath = "dhcp.log"
```

Trong dòng trên, chúng ta gán đường dẫn của tệp nhập ký mà chúng ta sẽ kiểm tra làm giá trị cho biến `$logPath` (đối với ví dụ trên là tệp này nằm cùng trong một thư mục với tập script)

```powershell
$events = Get-Content -Path $logPath |
Where-Object { $_ -match $macAddress } |
ForEach-Object { …
```

Trong đoạn trên, chúng ta thêm biến `$logPath` mà chúng ta vừa định nghĩa vào cmdlet `Get-Content` với tham số `-Path`. Sau đó, chúng ta sử dụng `|` để chuyển nội dung đọc được vào cmdlet `Where-Object` với tham số `-match $macAddress`. Mục tiêu ở đây là tìm kiếm giá trị mà chúng ta đã định nghĩa trước đó trong biến `$macAddress` trong dữ liệu đến từ tệp nhật ký.

Ở bước tiếp theo, chúng ta chuyển kết quả thu được bằng cách sử dụng một `|` thứ hai cho cmdlet `ForEach-Object`. Bây giờ chúng ta có các hàng chứa địa chỉ MAC mà chúng ta đã chỉ định. Giờ đây, chúng ta có thể xử lý dữ liệu này:

{% code overflow="wrap" %}

```powershell
ForEach-Object {
$fields = $_.Split(',')
$eventDate = [DateTime]::ParseExact($fields[1], "MM/dd/yy HH:mm:ss", $null)
$eventType = $fields[2]
$ipAddress = $fields[3]
$deviceName = $fields[4]
$mac = $fields[5]
```

{% endcode %}

Trong phần này, chúng ta chia mỗi hàng dữ liệu nhận được thành các cột bằng ký tự “,” và thêm từng phần vào biến `$fields` dưới dạng một mục của mảng.

Tiếp theo, chúng ta gán các trường này vào các biến để sử dụng sau. Lưu ý rằng ở đây, chúng ta đã chuyển đổi dữ liệu trong trường `$fields[1]` từ dạng chuỗi sang kiểu dữ liệu DateTime. Chúng ta làm điều này để có thể sắp xếp đầu ra một cách chính xác theo thứ tự thời gian khi in ra màn hình.

Chúng ta tiếp tục bằng cách kiểm tra xem dữ liệu mà chúng ta đang xử lý có chứa địa chỉ MAC mà chúng ta đang tìm kiếm hay không:

```powershell
if ($mac -eq $macAddress) {
	New-Object -TypeName PSObject -Property @{
		EventDate = $eventDate
		EventType = $eventType
		IPAddress = $ipAddress
		DeviceName = $deviceName
		MACAddress = $mac
		}
}
```

Nếu địa chỉ MAC tìm thấy trong dòng (giá trị của biến `$mac`) bằng với địa chỉ MAC (`$macAddress`) mà chúng ta đã định nghĩa ở đầu script (sử dụng `-eq`), chúng ta sẽ ghi nội dung của dòng này vào một đối tượng PowerShell mới.

```powershell
$events | Sort-Object -Property EventDate
```

Trong dòng cuối cùng, chúng ta gán các sự kiện mà chúng ta có (nội dung của biến `$events`) cho cmdlet `Sort-Object -Property` để sắp xếp chúng theo cột `EventDate`.

Và khi chúng ta chạy script của mình, kết quả nhận được như sau:

<figure><img src="/files/CJGici8AnQqingV4IepB" alt=""><figcaption></figcaption></figure>

Trong quá trình chuyển đổi dữ liệu mà chúng ta tìm kiếm thành kiểu dữ liệu đối tượng PowerShell, chúng ta đã nói rằng làm như vậy để có thể sắp xếp dữ liệu theo ngày. Khi dữ liệu đã được chuyển đổi sang kiểu đối tượng, việc sử dụng nó theo nhiều cách khác nhau trở nên rất dễ dàng. Ví dụ, giả sử chúng ta muốn chuyển đổi dữ liệu này sang định dạng JSON. Để thực hiện điều này, chúng ta chỉ cần chuyển dữ liệu mà chúng ta có cho cmdlet `ConvertTo-Json`.

{% code overflow="wrap" %}

```powershell
$events | Sort-Object -Property EventDate | ConvertTo-Json
```

{% endcode %}

Khi thực hiện điều này, đầu ra của chúng ta sẽ trông như sau:

<figure><img src="/files/pF4O7OKoPIUID5ySX3Pn" alt=""><figcaption></figcaption></figure>

Nếu chúng ta cần các loại đầu ra khác nhau, có thể sử dụng các cmdlet như `ConvertTo-Csv`, `ConvertTo-Xml`, `ConvertTo-Html` thay vì `ConvertTo-Json`.

Nếu chúng ta muốn ghi đầu ra thu được ở bất kỳ định dạng nào vào một tệp thay vì hiển thị trên màn hình, tất cả những gì chúng ta cần làm là gán đầu ra cho cmdlet `Set-Content` và chỉ định tệp mà chúng ta muốn ghi vào bằng tham số `-Path`.

{% code overflow="wrap" %}

```powershell
$events | Sort-Object -Property EventDate | ConvertTo-Json | Set-Content -Path "result.json"
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://viettaliii.gitbook.io/home/education/window-pe-.net/powershell/powershell-for-analysis/analysis-of-log-files-with-powershell.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
