最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Python構(gòu)建一個高效的日志處理系統(tǒng)

 更新時間:2025年07月16日 15:46:14   作者:nightunderblackcat  
這篇文章主要為大家詳細講解了如何使用Python開發(fā)一個專業(yè)的日志分析工具,能夠自動化處理、分析和可視化各類日志文件,大幅提升運維效率,需要的可以了解下

環(huán)境準備

開發(fā)本工具需要以下環(huán)境配置:

Python環(huán)境:建議Python 3.8或更高版本

必要庫

  • pandas:數(shù)據(jù)分析
  • matplotlib:數(shù)據(jù)可視化
  • numpy:數(shù)值計算
  • tqdm:進度條顯示
  • python-dateutil:日期解析

安裝命令:

pip install pandas matplotlib numpy tqdm python-dateutil

工具功能概述

本工具將實現(xiàn)以下核心功能:

  • 多格式日志文件解析(支持正則表達式配置)
  • 自動日志分類與統(tǒng)計
  • 錯誤模式識別與告警
  • 時間序列分析
  • 交互式可視化報表生成
  • 自定義分析規(guī)則支持

完整代碼實現(xiàn)

python
 
import re
import os
import gzip
import pandas as pd
import numpy as np
from datetime import datetime
from dateutil import parser
from tqdm import tqdm
import matplotlib.pyplot as plt
from typing import List, Dict, Tuple, Optional, Pattern
 
class LogAnalyzer:
    """專業(yè)的日志分析工具"""
    
    DEFAULT_PATTERNS = {
        'timestamp': r'(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})',
        'level': r'(?P<level>DEBUG|INFO|WARNING|ERROR|CRITICAL)',
        'message': r'(?P<message>.*)',
        'source': r'(?P<source>\w+\.\w+)'
    }
    
    def __init__(self, log_dir: str, output_dir: str = "log_analysis"):
        """
        初始化日志分析器
        
        :param log_dir: 日志目錄路徑
        :param output_dir: 輸出目錄路徑
        """
        self.log_dir = log_dir
        self.output_dir = output_dir
        os.makedirs(self.output_dir, exist_ok=True)
        
        # 編譯正則表達式
        self.patterns = {
            name: re.compile(pattern) 
            for name, pattern in self.DEFAULT_PATTERNS.items()
        }
        
        # 分析結(jié)果存儲
        self.stats = {
            'total_lines': 0,
            'level_counts': {},
            'source_counts': {},
            'errors': [],
            'timeline': []
        }
    
    def detect_log_format(self, sample_lines: List[str]) -> bool:
        """自動檢測日志格式"""
        for line in sample_lines[:10]:  # 檢查前10行
            match = self._parse_line(line)
            if not match:
                return False
        return True
    
    def _parse_line(self, line: str) -> Optional[Dict[str, str]]:
        """解析單行日志"""
        combined_pattern = re.compile(
            r'^{timestamp}\s+{level}\s+\[{source}\]\s+{message}$'.format(
                **self.DEFAULT_PATTERNS
            )
        )
        
        match = combined_pattern.match(line.strip())
        if match:
            return match.groupdict()
        return None
    
    def _read_log_file(self, filepath: str) -> List[str]:
        """讀取日志文件,支持gzip壓縮格式"""
        if filepath.endswith('.gz'):
            with gzip.open(filepath, 'rt', encoding='utf-8') as f:
                return f.readlines()
        else:
            with open(filepath, 'r', encoding='utf-8') as f:
                return f.readlines()
    
    def analyze_file(self, filepath: str):
        """分析單個日志文件"""
        lines = self._read_log_file(filepath)
        filename = os.path.basename(filepath)
        
        for line in tqdm(lines, desc=f"分析 {filename}"):
            self.stats['total_lines'] += 1
            parsed = self._parse_line(line)
            
            if not parsed:
                continue  # 跳過無法解析的行
                
            # 更新時間線數(shù)據(jù)
            try:
                dt = parser.parse(parsed['timestamp'])
                self.stats['timeline'].append({
                    'timestamp': dt,
                    'level': parsed['level'],
                    'source': parsed['source']
                })
            except (ValueError, KeyError):
                pass
            
            # 統(tǒng)計日志級別
            level = parsed.get('level', 'UNKNOWN')
            self.stats['level_counts'][level] = self.stats['level_counts'].get(level, 0) + 1
            
            # 統(tǒng)計來源
            source = parsed.get('source', 'unknown')
            self.stats['source_counts'][source] = self.stats['source_counts'].get(source, 0) + 1
            
            # 記錄錯誤信息
            if level in ('ERROR', 'CRITICAL'):
                self.stats['errors'].append({
                    'timestamp': parsed.get('timestamp'),
                    'source': source,
                    'message': parsed.get('message', '')[:500]  # 截斷長消息
                })
    
    def analyze_directory(self):
        """分析目錄下所有日志文件"""
        log_files = []
        for root, _, files in os.walk(self.log_dir):
            for file in files:
                if file.endswith(('.log', '.txt', '.gz')):
                    log_files.append(os.path.join(root, file))
        
        print(f"發(fā)現(xiàn) {len(log_files)} 個日志文件待分析...")
        for filepath in log_files:
            self.analyze_file(filepath)
    
    def generate_reports(self):
        """生成分析報告"""
        # 準備時間序列數(shù)據(jù)
        timeline_df = pd.DataFrame(self.stats['timeline'])
        timeline_df.set_index('timestamp', inplace=True)
        
        # 1. 生成日志級別分布圖
        self._plot_level_distribution()
        
        # 2. 生成時間序列圖
        self._plot_timeline(timeline_df)
        
        # 3. 生成錯誤報告
        self._generate_error_report()
        
        # 4. 保存統(tǒng)計結(jié)果
        self._save_statistics()
    
    def _plot_level_distribution(self):
        """繪制日志級別分布圖"""
        levels = list(self.stats['level_counts'].keys())
        counts = list(self.stats['level_counts'].values())
        
        plt.figure(figsize=(10, 6))
        bars = plt.bar(levels, counts, color=['green', 'blue', 'orange', 'red', 'purple'])
        
        # 添加數(shù)值標簽
        for bar in bars:
            height = bar.get_height()
            plt.text(bar.get_x() + bar.get_width()/2., height,
                    f'{height:,}', ha='center', va='bottom')
        
        plt.title('日志級別分布')
        plt.xlabel('日志級別')
        plt.ylabel('出現(xiàn)次數(shù)')
        plt.grid(axis='y', linestyle='--', alpha=0.7)
        
        # 保存圖片
        output_path = os.path.join(self.output_dir, 'level_distribution.png')
        plt.savefig(output_path, bbox_inches='tight', dpi=300)
        plt.close()
        print(f"已保存日志級別分布圖: {output_path}")
    
    def _plot_timeline(self, df: pd.DataFrame):
        """繪制時間序列圖"""
        plt.figure(figsize=(14, 8))
        
        # 按小時重采樣
        hourly = df.groupby([pd.Grouper(freq='H'), 'level']).size().unstack()
        hourly.plot(kind='area', stacked=True, alpha=0.7, figsize=(14, 8))
        
        plt.title('日志活動時間線(按小時)')
        plt.xlabel('時間')
        plt.ylabel('日志數(shù)量')
        plt.grid(True, linestyle='--', alpha=0.5)
        plt.legend(title='日志級別')
        
        # 保存圖片
        output_path = os.path.join(self.output_dir, 'activity_timeline.png')
        plt.savefig(output_path, bbox_inches='tight', dpi=300)
        plt.close()
        print(f"已保存活動時間線圖: {output_path}")
    
    def _generate_error_report(self):
        """生成錯誤報告"""
        if not self.stats['errors']:
            print("未發(fā)現(xiàn)錯誤日志")
            return
            
        df = pd.DataFrame(self.stats['errors'])
        
        # 按錯誤源分組統(tǒng)計
        error_stats = df.groupby('source').size().sort_values(ascending=False)
        
        # 保存CSV
        csv_path = os.path.join(self.output_dir, 'error_report.csv')
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
        
        # 生成錯誤源分布圖
        plt.figure(figsize=(12, 6))
        error_stats.plot(kind='bar', color='coral')
        plt.title('錯誤來源分布')
        plt.xlabel('來源組件')
        plt.ylabel('錯誤數(shù)量')
        plt.grid(axis='y', linestyle='--', alpha=0.7)
        
        img_path = os.path.join(self.output_dir, 'error_source_distribution.png')
        plt.savefig(img_path, bbox_inches='tight', dpi=300)
        plt.close()
        
        print(f"已生成錯誤報告:\n- CSV文件: {csv_path}\n- 分布圖: {img_path}")
    
    def _save_statistics(self):
        """保存統(tǒng)計結(jié)果"""
        stats_path = os.path.join(self.output_dir, 'summary_statistics.txt')
        
        with open(stats_path, 'w', encoding='utf-8') as f:
            f.write("=== 日志分析摘要 ===\n\n")
            f.write(f"分析時間: {datetime.now().isoformat()}\n")
            f.write(f"日志目錄: {self.log_dir}\n")
            f.write(f"分析日志行數(shù): {self.stats['total_lines']:,}\n\n")
            
            f.write("日志級別統(tǒng)計:\n")
            for level, count in sorted(self.stats['level_counts'].items()):
                f.write(f"- {level}: {count:,} ({count/self.stats['total_lines']:.1%})\n")
            
            f.write("\n來源組件統(tǒng)計 (Top 10):\n")
            top_sources = sorted(
                self.stats['source_counts'].items(), 
                key=lambda x: x[1], 
                reverse=True
            )[:10]
            for source, count in top_sources:
                f.write(f"- {source}: {count:,}\n")
            
            f.write(f"\n發(fā)現(xiàn)錯誤數(shù)量: {len(self.stats['errors'])}\n")
        
        print(f"已保存統(tǒng)計摘要: {stats_path}")
 
# 使用示例
if __name__ == "__main__":
    # 配置日志目錄路徑
    LOG_DIRECTORY = "/var/log/myapp"
    
    # 初始化分析器
    analyzer = LogAnalyzer(LOG_DIRECTORY)
    
    # 執(zhí)行分析
    print("開始日志分析...")
    analyzer.analyze_directory()
    
    # 生成報告
    print("\n生成分析報告...")
    analyzer.generate_reports()
    
    print("\n分析完成!所有報告已保存至:", analyzer.output_dir)

代碼深度解析

1. 類設計與初始化

class LogAnalyzer:
    DEFAULT_PATTERNS = {
        'timestamp': r'(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})',
        'level': r'(?P<level>DEBUG|INFO|WARNING|ERROR|CRITICAL)',
        'message': r'(?P<message>.*)',
        'source': r'(?P<source>\w+\.\w+)'
    }
    
    def __init__(self, log_dir: str, output_dir: str = "log_analysis"):
        self.log_dir = log_dir
        self.output_dir = output_dir
        os.makedirs(self.output_dir, exist_ok=True)
        
        self.patterns = {
            name: re.compile(pattern) 
            for name, pattern in self.DEFAULT_PATTERNS.items()
        }
        
        self.stats = {
            'total_lines': 0,
            'level_counts': {},
            'source_counts': {},
            'errors': [],
            'timeline': []
        }

預定義常見日志格式的正則表達式模式

支持自定義輸出目錄,自動創(chuàng)建目錄

編譯正則表達式提升匹配效率

初始化統(tǒng)計數(shù)據(jù)結(jié)構(gòu),包括:

  • 總行數(shù)統(tǒng)計
  • 日志級別計數(shù)
  • 來源組件計數(shù)
  • 錯誤日志收集
  • 時間線數(shù)據(jù)

2. 日志解析核心邏輯

def _parse_line(self, line: str) -> Optional[Dict[str, str]]:
    combined_pattern = re.compile(
        r'^{timestamp}\s+{level}\s+\[{source}\]\s+{message}$'.format(
            **self.DEFAULT_PATTERNS
        )
    )
    
    match = combined_pattern.match(line.strip())
    if match:
        return match.groupdict()
    return None

組合多個正則模式構(gòu)建完整日志解析器

使用命名捕獲組(?P<name>...)提取結(jié)構(gòu)化字段

返回包含各字段的字典或None(解析失敗時)

示例匹配格式:2023-01-01 12:00:00,123 INFO [module.submodule] This is a log message

3. 文件處理與進度顯示

def _read_log_file(self, filepath: str) -> List[str]:
    if filepath.endswith('.gz'):
        with gzip.open(filepath, 'rt', encoding='utf-8') as f:
            return f.readlines()
    else:
        with open(filepath, 'r', encoding='utf-8') as f:
            return f.readlines()
 
def analyze_file(self, filepath: str):
    lines = self._read_log_file(filepath)
    filename = os.path.basename(filepath)
    
    for line in tqdm(lines, desc=f"分析 {filename}"):
        self.stats['total_lines'] += 1
        parsed = self._parse_line(line)
        
        if not parsed:
            continue
        # ...分析邏輯...
  • 自動處理gzip壓縮日志文件
  • 使用tqdm顯示進度條,提升用戶體驗
  • 統(tǒng)一UTF-8編碼處理,避免編碼問題
  • 跳過無法解析的日志行(記錄總數(shù)仍會增加)

4. 時間序列處理

# 在analyze_file方法中
try:
    dt = parser.parse(parsed['timestamp'])
    self.stats['timeline'].append({
        'timestamp': dt,
        'level': parsed['level'],
        'source': parsed['source']
    })
except (ValueError, KeyError):
    pass
 
# 在generate_reports方法中
timeline_df = pd.DataFrame(self.stats['timeline'])
timeline_df.set_index('timestamp', inplace=True)
  • 使用dateutil.parser智能解析各種時間格式
  • 構(gòu)建時間線數(shù)據(jù)結(jié)構(gòu),保留日志級別和來源信息
  • 轉(zhuǎn)換為Pandas DataFrame便于時間序列分析
  • 自動處理時間解析錯誤,不影響主流程

5. 可視化報表生成

def _plot_level_distribution(self):
    levels = list(self.stats['level_counts'].keys())
    counts = list(self.stats['level_counts'].values())
    
    plt.figure(figsize=(10, 6))
    bars = plt.bar(levels, counts, color=['green', 'blue', 'orange', 'red', 'purple'])
    
    # 添加數(shù)值標簽
    for bar in bars:
        height = bar.get_height()
        plt.text(bar.get_x() + bar.get_width()/2., height,
                f'{height:,}', ha='center', va='bottom')
    # ...保存圖片...
  • 使用matplotlib創(chuàng)建專業(yè)級圖表
  • 自動為不同日志級別分配直觀顏色
  • 在柱狀圖上顯示精確數(shù)值
  • 配置網(wǎng)格線、標題等圖表元素
  • 保存高DPI圖片,適合報告使用

高級應用與擴展

1. 多日志格式支持

def add_log_format(self, name: str, pattern: str):
    """添加自定義日志格式"""
    try:
        self.patterns[name] = re.compile(pattern)
    except re.error as e:
        print(f"無效的正則表達式: {pattern} - {str(e)}")
 
def auto_detect_format(self, sample_lines: List[str]) -> bool:
    """自動檢測日志格式"""
    common_formats = [
        (r'^(?P<timestamp>.+?) (?P<level>\w+) (?P<message>.+)$', "格式A"),
        (r'^\[(?P<timestamp>.+?)\] \[(?P<level>\w+)\] (?P<source>\w+) - (?P<message>.+)$', "格式B")
    ]
    
    for pattern, name in common_formats:
        matched = 0
        for line in sample_lines[:10]:  # 檢查前10行
            if re.match(pattern, line.strip()):
                matched += 1
        
        if matched >= 8:  # 80%匹配則認為成功
            self.add_log_format(name, pattern)
            return True
    return False

2. 異常模式檢測

def detect_anomalies(self, window_size: int = 60, threshold: int = 10):
    """檢測異常錯誤爆發(fā)"""
    df = pd.DataFrame(self.stats['timeline'])
    error_df = df[df['level'].isin(['ERROR', 'CRITICAL'])]
    
    # 按分鐘統(tǒng)計錯誤數(shù)
    error_counts = error_df.resample('1T', on='timestamp').size()
    
    # 使用滑動窗口檢測異常
    rolling_mean = error_counts.rolling(window=window_size).mean()
    anomalies = error_counts[error_counts > (rolling_mean + threshold)]
    
    if not anomalies.empty:
        report = "\n".join(
            f"{ts}: {count} 個錯誤 (平均: {rolling_mean[ts]:.1f})"
            for ts, count in anomalies.items()
        )
        print(f"檢測到異常錯誤爆發(fā):\n{report}")
        
        # 保存異常報告
        with open(os.path.join(self.output_dir, 'anomalies.txt'), 'w') as f:
            f.write(report)

3. 日志歸檔與輪轉(zhuǎn)支持

def handle_rotated_logs(self):
    """處理輪轉(zhuǎn)的日志文件"""
    for root, _, files in os.walk(self.log_dir):
        for file in files:
            if re.match(r'.*\.[0-9]+(\.gz)?$', file):  # 匹配輪轉(zhuǎn)文件如.log.1, .log.2.gz
                filepath = os.path.join(root, file)
                self.analyze_file(filepath)

性能優(yōu)化建議

1.多進程處理

from concurrent.futures import ProcessPoolExecutor
 
def parallel_analyze(self):
    log_files = self._find_log_files()
    with ProcessPoolExecutor() as executor:
        list(tqdm(executor.map(self.analyze_file, log_files), total=len(log_files)))

2.內(nèi)存優(yōu)化

  • 逐行處理大文件而非全量讀取
  • 定期將結(jié)果寫入磁盤

3.索引與緩存

  • 為已分析文件創(chuàng)建哈希索引
  • 僅分析新增或修改的內(nèi)容

安全注意事項

1.日志文件驗證

  • 檢查文件權(quán)限
  • 驗證文件確實是文本格式

2.敏感信息處理

  • 可選過濾敏感字段(密碼、密鑰等)
  • 支持數(shù)據(jù)脫敏

3.資源限制

  • 限制最大文件大小
  • 控制并發(fā)分析任務數(shù)

單元測試建議

import unittest
import tempfile
import shutil
from pathlib import Path
 
class TestLogAnalyzer(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.test_dir = Path(tempfile.mkdtemp())
        cls.sample_log = cls.test_dir / "test.log"
        
        # 創(chuàng)建測試日志文件
        with open(cls.sample_log, 'w') as f:
            f.write("2023-01-01 12:00:00,123 INFO [app.core] System started\n")
            f.write("2023-01-01 12:00:01,456 ERROR [app.db] Connection failed\n")
    
    def test_parser(self):
        analyzer = LogAnalyzer(self.test_dir)
        parsed = analyzer._parse_line("2023-01-01 12:00:00,123 INFO [app.core] Test message")
        self.assertEqual(parsed['level'], 'INFO')
        self.assertEqual(parsed['source'], 'app.core')
    
    def test_analysis(self):
        analyzer = LogAnalyzer(self.test_dir)
        analyzer.analyze_file(self.sample_log)
        self.assertEqual(analyzer.stats['total_lines'], 2)
        self.assertEqual(analyzer.stats['level_counts']['INFO'], 1)
        self.assertEqual(analyzer.stats['level_counts']['ERROR'], 1)
    
    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(cls.test_dir)
 
if __name__ == '__main__':
    unittest.main()

結(jié)語

本文詳細講解了專業(yè)日志分析工具的開發(fā)過程,涵蓋了:

  • 多格式日志解析技術(shù)
  • 高效文件處理與進度顯示
  • 時間序列分析方法
  • 自動化報表生成
  • 可視化圖表創(chuàng)建

讀者可以通過此基礎(chǔ)框架擴展以下高級功能:

  • 集成機器學習異常檢測
  • 開發(fā)Web監(jiān)控界面
  • 添加實時日志監(jiān)控能力
  • 支持分布式日志收集
  • 構(gòu)建自動化告警系統(tǒng)

建議在實際部署前充分測試各種日志格式,并根據(jù)具體業(yè)務需求調(diào)整分析規(guī)則。此工具可廣泛應用于:

  • 應用程序性能監(jiān)控
  • 系統(tǒng)故障診斷
  • 安全審計分析
  • 用戶行為分析等場景

到此這篇關(guān)于使用Python構(gòu)建一個高效的日志處理系統(tǒng)的文章就介紹到這了,更多相關(guān)Python日志處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python股票數(shù)據(jù)可視化代碼詳解

    Python股票數(shù)據(jù)可視化代碼詳解

    這篇文章主要為大家詳細介紹了Python股票數(shù)據(jù)可視化,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Python SQLAlchemy簡介及基本用法

    Python SQLAlchemy簡介及基本用法

    SQLAlchemy是一個基于Python實現(xiàn)的ORM對象關(guān)系映射框架,該框架建立在DB API之上,使用關(guān)系對象映射進行數(shù)據(jù)庫操作,這篇文章主要介紹了SQLAlchemy簡介以及基本使用,需要的朋友可以參考下
    2023-08-08
  • Python網(wǎng)頁正文轉(zhuǎn)換語音文件的操作方法

    Python網(wǎng)頁正文轉(zhuǎn)換語音文件的操作方法

    這篇文章主要介紹了Python網(wǎng)頁正文轉(zhuǎn)換語音文件的操作方法,需要的朋友可以參考下
    2018-12-12
  • Django+vue+vscode前后端分離搭建的實現(xiàn)

    Django+vue+vscode前后端分離搭建的實現(xiàn)

    本文以一個非常簡單的demo為例,介紹了利用django+drf+vue的前后端分離開發(fā)模式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2023-08-08
  • 解決Python 命令行執(zhí)行腳本時,提示導入的包找不到的問題

    解決Python 命令行執(zhí)行腳本時,提示導入的包找不到的問題

    今天小編就為大家分享一篇解決Python 命令行執(zhí)行腳本時,提示導入的包找不到的問題,具有很好的參考價值,希望對大家有所幫助,一起跟隨小編過來看看吧
    2019-01-01
  • Python實現(xiàn)微信好友的數(shù)據(jù)分析

    Python實現(xiàn)微信好友的數(shù)據(jù)分析

    這篇文章主要為大家詳細介紹了Python實現(xiàn)微信好友的數(shù)據(jù)分析,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • python多線程并發(fā)實例及其優(yōu)化

    python多線程并發(fā)實例及其優(yōu)化

    這篇文章主要介紹了python多線程并發(fā)實例及其優(yōu)化,threading是擴展模塊,在thread的基礎(chǔ)上進行了封裝及改進。所以只需要使用threading這個模塊就能完成并發(fā)的測試,需要的朋友可以參考下
    2019-06-06
  • 使用Pyinstaller打包exe文件詳細圖文教程

    使用Pyinstaller打包exe文件詳細圖文教程

    PyInstaller可以用來打包python應用程序,打包完的程序就可以在沒有安裝Python解釋器的機器上運行了,下面這篇文章主要給大家介紹了關(guān)于使用Pyinstaller打包exe文件的詳細圖文教程,需要的朋友可以參考下
    2022-08-08
  • 使用pyinstaller打包python PyQt5程序

    使用pyinstaller打包python PyQt5程序

    當你寫好一個python應用以后(有可能是命令行,有可能是GUI),你或許希望分享給他人使用,而別人可能并沒有python環(huán)境,那么我們需要尋找一種方法生成可執(zhí)行文件(比如Windows上的exe或macOs上的app)
    2021-10-10
  • Python常用的json標準庫

    Python常用的json標準庫

    今天小編就為大家分享一篇關(guān)于Python常用的json標準庫,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02

最新評論

毕节市| 双城市| 乳山市| 凤冈县| 榆林市| 上蔡县| 靖州| 扬州市| 运城市| 中牟县| 梁平县| 古浪县| 定兴县| 辽阳市| 和顺县| 哈密市| 浪卡子县| 崇仁县| 白玉县| 沾化县| 金溪县| 禹城市| 兰西县| 白银市| 石嘴山市| 贵阳市| 石台县| 大田县| 梨树县| 冕宁县| 邓州市| 华池县| 太白县| 永和县| 镇雄县| 东山县| 泸水县| 海兴县| 徐州市| 清新县| 和硕县|