20-bo‘lim

Yakuniy loyiha

Yigirma bo'lim bilimini bitta ishlaydigan dasturda birlashtirish - jurnal tahlilchisi.

🕑 11 daqiqa o‘qish 📄 672 so‘z 👁 4 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Loyiha tuzilishi
  2. 1-qadam: ma'lumot turi va ajratuvchi
  3. 2-qadam: ajratish va xatolar
  4. 3-qadam: filtrlar
  5. 4-qadam: statistika
  6. 5-qadam: to'liq dastur
  7. Bundan keyin nima
  8. Xulosa

Bu bo'limda haqiqiy dastur quramiz: server jurnalini tahlil qiluvchi vosita. U kirish faylini o'qiydi, qatorlarni ajratadi, statistika to'playdi va hisobot chiqaradi.

Har bir qadamda oldingi bo'limlarning qaysi biri ishlatilganini ko'rsatamiz.

Loyiha tuzilishi #

C++
#include <print>
#include <string>
#include <string_view>
#include <vector>
#include <map>
#include <optional>
#include <expected>
#include <memory>
#include <algorithm>
#include <ranges>
#include <numeric>
#include <chrono>
#include <fstream>
#include <filesystem>
#include <stdexcept>
#include <charconv>
#include <concepts>
C++
int main()
{
    std::println("JURNAL TAHLILCHISI - qismlar:");
    std::println("");
    std::println("{:<22} {:<28} {}", "QISM", "VAZIFA", "BO'LIM");
    std::println("{}", std::string(72, '-'));
    std::println("{:<22} {:<28} {}", "Qator", "Bitta jurnal yozuvi", "11 sinflar");
    std::println("{:<22} {:<28} {}", "QatorXatosi", "Ajratish xatosi sababi", "16 xatolar");
    std::println("{:<22} {:<28} {}", "ajrat()", "Matndan Qator yasash", "6 satrlar");
    std::println("{:<22} {:<28} {}", "Statistika", "To'plangan hisob", "7 konteynerlar");
    std::println("{:<22} {:<28} {}", "Filtr", "Interfeys (abstrakt)", "14 polimorfizm");
    std::println("{:<22} {:<28} {}", "Hisobot", "Chiqarish formati", "13 operatorlar");
    std::println("{:<22} {:<28} {}", "Tahlilchi", "Hammasini birlashtiradi", "9 RAII");

    std::println("");
    std::println("Ishlatiladigan tamoyillar:");
    std::println("  - Qiymat semantikasi, ko'chirish (12-bo'lim)");
    std::println("  - std::expected bilan xato uzatish (16-bo'lim)");
    std::println("  - unique_ptr bilan polimorf filtrlar (10-bo'lim)");
    std::println("  - ranges bilan ma'lumot oqimi (8-bo'lim)");
    std::println("  - Konsept bilan cheklangan shablon (15-bo'lim)");

    return 0;
}
Natija
JURNAL TAHLILCHISI - qismlar:

QISM                   VAZIFA                       BO'LIM
------------------------------------------------------------------------
Qator                  Bitta jurnal yozuvi          11 sinflar
QatorXatosi            Ajratish xatosi sababi       16 xatolar
ajrat()                Matndan Qator yasash         6 satrlar
Statistika             To'plangan hisob             7 konteynerlar
Filtr                  Interfeys (abstrakt)         14 polimorfizm
Hisobot                Chiqarish formati            13 operatorlar
Tahlilchi              Hammasini birlashtiradi      9 RAII

Ishlatiladigan tamoyillar:
  - Qiymat semantikasi, ko'chirish (12-bo'lim)
  - std::expected bilan xato uzatish (16-bo'lim)
  - unique_ptr bilan polimorf filtrlar (10-bo'lim)
  - ranges bilan ma'lumot oqimi (8-bo'lim)
  - Konsept bilan cheklangan shablon (15-bo'lim)

1-qadam: ma'lumot turi va ajratuvchi #

C++
enum class Daraja { Nosozlik, Ogohlantirish, Xato, Halokat };

std::string darajaMatni(Daraja d)
{
    switch (d) {
        case Daraja::Nosozlik:      return "NOSOZLIK";
        case Daraja::Ogohlantirish: return "OGOHLANTIRISH";
        case Daraja::Xato:          return "XATO";
        case Daraja::Halokat:       return "HALOKAT";
    }
    return "?";
}

std::optional<Daraja> darajaOqi(std::string_view s)
{
    if (s == "INFO")  return Daraja::Nosozlik;
    if (s == "WARN")  return Daraja::Ogohlantirish;
    if (s == "ERROR") return Daraja::Xato;
    if (s == "FATAL") return Daraja::Halokat;
    return std::nullopt;
}

// Bitta jurnal yozuvi - qiymat turi, nol qoidasi (12-bo'lim)
struct Qator
{
    std::string sana;
    Daraja daraja{};
    std::string manba;
    std::string xabar;
    int davomiylik{};        // millisekundda

    auto operator<=>(const Qator&) const = default;
};

int main()
{
    Qator q{"2024-09-01", Daraja::Xato, "auth", "parol noto'g'ri", 120};

    std::println("sana:        {}", q.sana);
    std::println("daraja:      {}", darajaMatni(q.daraja));
    std::println("manba:       {}", q.manba);
    std::println("xabar:       {}", q.xabar);
    std::println("davomiylik:  {} ms", q.davomiylik);

    std::println("");
    Qator nusxa = q;
    std::println("nusxa tengmi: {}", nusxa == q);

    nusxa.davomiylik = 200;
    std::println("o'zgartirgandan keyin tengmi: {}", nusxa == q);
    std::println("asl o'zgardimi: {} ms", q.davomiylik);

    std::println("");
    std::println("Daraja o'qish:");
    for (std::string_view s : {"INFO", "ERROR", "TRACE"}) {
        auto d = darajaOqi(s);
        std::println("  {:<6} -> {}", s, d ? darajaMatni(*d) : "noma'lum");
    }

    return 0;
}
Natija
sana:        2024-09-01
daraja:      XATO
manba:       auth
xabar:       parol noto'g'ri
davomiylik:  120 ms

nusxa tengmi: true
o'zgartirgandan keyin tengmi: false
asl o'zgardimi: 120 ms

Daraja o'qish:
  INFO   -> NOSOZLIK
  ERROR  -> XATO
  TRACE  -> noma'lum

2-qadam: ajratish va xatolar #

C++
enum class Daraja { Nosozlik, Ogohlantirish, Xato, Halokat };

std::string darajaMatni(Daraja d)
{
    switch (d) {
        case Daraja::Nosozlik:      return "NOSOZLIK";
        case Daraja::Ogohlantirish: return "OGOHLANTIRISH";
        case Daraja::Xato:          return "XATO";
        case Daraja::Halokat:       return "HALOKAT";
    }
    return "?";
}

std::optional<Daraja> darajaOqi(std::string_view s)
{
    if (s == "INFO")  return Daraja::Nosozlik;
    if (s == "WARN")  return Daraja::Ogohlantirish;
    if (s == "ERROR") return Daraja::Xato;
    if (s == "FATAL") return Daraja::Halokat;
    return std::nullopt;
}

struct Qator
{
    std::string sana;
    Daraja daraja{};
    std::string manba;
    std::string xabar;
    int davomiylik{};
};

// Xato SABABI ham kerak - shuning uchun expected (16-bo'lim)
enum class QatorXatosi { BoshQator, KamMaydon, NomalumDaraja, YaroqsizVaqt };

std::string xatoMatni(QatorXatosi x)
{
    switch (x) {
        case QatorXatosi::BoshQator:     return "qator bo'sh";
        case QatorXatosi::KamMaydon:     return "maydonlar yetarli emas";
        case QatorXatosi::NomalumDaraja: return "daraja tanilmadi";
        case QatorXatosi::YaroqsizVaqt:  return "davomiylik son emas";
    }
    return "?";
}

std::vector<std::string_view> bolaklar(std::string_view s, char ajratgich, int soni)
{
    std::vector<std::string_view> natija;
    while (soni > 1) {
        auto joy = s.find(ajratgich);
        if (joy == std::string_view::npos) break;
        natija.push_back(s.substr(0, joy));
        s.remove_prefix(joy + 1);
        --soni;
    }
    natija.push_back(s);
    return natija;
}

std::expected<Qator, QatorXatosi> ajrat(std::string_view qator)
{
    if (qator.empty()) return std::unexpected{QatorXatosi::BoshQator};

    auto b = bolaklar(qator, '|', 5);
    if (b.size() < 5) return std::unexpected{QatorXatosi::KamMaydon};

    auto daraja = darajaOqi(b[1]);
    if (!daraja) return std::unexpected{QatorXatosi::NomalumDaraja};

    int ms{};
    auto [oxir, xato] = std::from_chars(b[4].data(), b[4].data() + b[4].size(), ms);
    if (xato != std::errc{}) return std::unexpected{QatorXatosi::YaroqsizVaqt};

    return Qator{std::string{b[0]}, *daraja, std::string{b[2]},
                 std::string{b[3]}, ms};
}

int main()
{
    std::vector<std::string> sinovlar = {
        "2024-09-01|INFO|auth|foydalanuvchi kirdi|42",
        "2024-09-01|ERROR|db|ulanish uzildi|1500",
        "",
        "2024-09-01|TRACE|api|nomalum daraja|10",
        "2024-09-01|WARN|api",
        "2024-09-01|INFO|api|so'rov|tez",
    };

    for (const auto& s : sinovlar) {
        auto n = ajrat(s);
        std::string qisqa = s.size() > 34 ? s.substr(0, 31) + "..." : s;
        if (n) {
            std::println("  {:<36} OK   {} / {} ms",
                         std::format("'{}'", qisqa), darajaMatni(n->daraja),
                         n->davomiylik);
        } else {
            std::println("  {:<36} XATO {}",
                         std::format("'{}'", qisqa), xatoMatni(n.error()));
        }
    }

    return 0;
}
Natija
  '2024-09-01|INFO|auth|foydalanuv...' OK   NOSOZLIK / 42 ms
  '2024-09-01|ERROR|db|ulanish uzi...' OK   XATO / 1500 ms
  ''                                   XATO qator bo'sh
  '2024-09-01|TRACE|api|nomalum da...' XATO daraja tanilmadi
  '2024-09-01|WARN|api'                XATO maydonlar yetarli emas
  '2024-09-01|INFO|api|so'rov|tez'     XATO davomiylik son emas
string_view bilan ajratish - nusxasiz

bolaklar() funksiyasi std::string_view qaytaradi - ya'ni hech qanday satr nusxalanmaydi. U faqat asl matnning bo'laklariga ishora qiladi.

Nusxa faqat oxirida, Qator qurilayotganda olinadi:

KOD
return Qator{std::string{b[0]}, ...};

Bu 6-bo'limdagi qoidaning amaliy qo'llanishi: o'qish uchun string_view, saqlash uchun string.

Diqqat: string_view egalik olmaydi. Agar asl satr yo'q qilinsa yoki o'zgartirilsa - string_view osilgan bo'lib qoladi (19-bo'lim).

Shuning uchun ajrat() string_view qaytarmaydi - u Qator ni qiymat sifatida qaytaradi.

3-qadam: filtrlar #

C++
enum class Daraja { Nosozlik, Ogohlantirish, Xato, Halokat };

struct Qator
{
    std::string sana;
    Daraja daraja{};
    std::string manba;
    std::string xabar;
    int davomiylik{};
};

// Ish paytida tanlanadigan xatti-harakat -> virtual (14-bo'lim)
class Filtr
{
public:
    virtual ~Filtr() = default;
    virtual bool mos(const Qator& q) const = 0;
    virtual std::string tavsif() const = 0;
};

class DarajaFiltri : public Filtr
{
    Daraja eng_kam_;
public:
    explicit DarajaFiltri(Daraja d) : eng_kam_(d) {}
    bool mos(const Qator& q) const override { return q.daraja >= eng_kam_; }
    std::string tavsif() const override { return "daraja bo'yicha"; }
};

class ManbaFiltri : public Filtr
{
    std::string manba_;
public:
    explicit ManbaFiltri(std::string m) : manba_(std::move(m)) {}
    bool mos(const Qator& q) const override { return q.manba == manba_; }
    std::string tavsif() const override { return "manba = " + manba_; }
};

class SekinFiltri : public Filtr
{
    int chegara_;
public:
    explicit SekinFiltri(int ms) : chegara_(ms) {}
    bool mos(const Qator& q) const override { return q.davomiylik >= chegara_; }
    std::string tavsif() const override
    {
        return std::format("davomiylik >= {} ms", chegara_);
    }
};

// Kompozit - filtrlarni birlashtiradi
class HammaFiltr : public Filtr
{
    std::vector<std::unique_ptr<Filtr>> ichki_;
public:
    void qosh(std::unique_ptr<Filtr> f) { ichki_.push_back(std::move(f)); }

    bool mos(const Qator& q) const override
    {
        return std::ranges::all_of(ichki_,
            [&q](const auto& f) { return f->mos(q); });
    }

    std::string tavsif() const override
    {
        std::string s = "hammasi(";
        for (std::size_t i = 0; i < ichki_.size(); ++i) {
            if (i) s += ", ";
            s += ichki_[i]->tavsif();
        }
        return s + ")";
    }
};

int main()
{
    std::vector<Qator> jurnal = {
        {"2024-09-01", Daraja::Nosozlik,      "auth", "kirish",      42},
        {"2024-09-01", Daraja::Xato,          "db",   "ulanish",   1500},
        {"2024-09-01", Daraja::Ogohlantirish, "db",   "sekin",      900},
        {"2024-09-01", Daraja::Halokat,       "db",   "to'xtadi",  3000},
        {"2024-09-01", Daraja::Xato,          "api",  "404",         15},
    };

    std::vector<std::unique_ptr<Filtr>> filtrlar;
    filtrlar.push_back(std::make_unique<DarajaFiltri>(Daraja::Xato));
    filtrlar.push_back(std::make_unique<ManbaFiltri>("db"));
    filtrlar.push_back(std::make_unique<SekinFiltri>(1000));

    auto birlashgan = std::make_unique<HammaFiltr>();
    birlashgan->qosh(std::make_unique<DarajaFiltri>(Daraja::Xato));
    birlashgan->qosh(std::make_unique<ManbaFiltri>("db"));
    filtrlar.push_back(std::move(birlashgan));

    for (const auto& f : filtrlar) {
        auto mos = jurnal | std::views::filter(
            [&f](const Qator& q) { return f->mos(q); });
        auto soni = std::ranges::distance(mos);
        std::println("{:<38} {} ta qator", f->tavsif(), soni);
    }

    return 0;
}
Natija
daraja bo'yicha                        3 ta qator
manba = db                             3 ta qator
davomiylik >= 1000 ms                  2 ta qator
hammasi(daraja bo'yicha, manba = db)   2 ta qator
Ma'lumot oqimi va har bosqichda ishlatilgan vositalar FAYL ifstream RAII - 9-bo'lim AJRATISH string_view nusxasiz - 6-bo'lim TEKSHIRISH expected<Qator, E> sabab bilan - 16-bo'lim FILTRLASH unique_ptr<Filtr> + views 10, 14, 8-bo'limlar TO'PLASH map, accumulate 7, 8-bo'limlar HISOBOT format, formatter 13, 17-bo'limlar Butun dasturda birorta `new` yoki `delete` yo'q • Fayl - ifstream destruktori yopadi • Filtrlar - unique_ptr yo'q qiladi • Qatorlar - vector boshqaradi • Satrlar - string o'zi ajratadi Yig'ish va tekshirish g++ -std=c++23 -Wall -Wextra -Wpedantic -O2 -o tahlil tahlil.cpp g++ -std=c++23 -g -fsanitize=address,undefined -o tahlil_sinov tahlil.cpp Ikkinchisini sinovlarda ishga tushiring - UB jimgina o'tib ketmasin.
Har bosqichda tegishli vosita - va hech qanday qo'lda xotira boshqaruvi

4-qadam: statistika #

C++
enum class Daraja { Nosozlik, Ogohlantirish, Xato, Halokat };

std::string darajaMatni(Daraja d)
{
    switch (d) {
        case Daraja::Nosozlik:      return "NOSOZLIK";
        case Daraja::Ogohlantirish: return "OGOHLANTIRISH";
        case Daraja::Xato:          return "XATO";
        case Daraja::Halokat:       return "HALOKAT";
    }
    return "?";
}

struct Qator
{
    std::string sana;
    Daraja daraja{};
    std::string manba;
    std::string xabar;
    int davomiylik{};
};

class Statistika
{
    std::map<Daraja, int> daraja_;
    std::map<std::string, int> manba_;
    std::vector<int> davomiyliklar_;

public:
    void qosh(const Qator& q)
    {
        ++daraja_[q.daraja];
        ++manba_[q.manba];
        davomiyliklar_.push_back(q.davomiylik);
    }

    std::size_t soni() const { return davomiyliklar_.size(); }

    double ortacha() const
    {
        if (davomiyliklar_.empty()) return 0.0;
        long long jami = std::accumulate(davomiyliklar_.begin(),
                                         davomiyliklar_.end(), 0LL);
        return static_cast<double>(jami) /
               static_cast<double>(davomiyliklar_.size());
    }

    int median() const
    {
        if (davomiyliklar_.empty()) return 0;
        auto nusxa = davomiyliklar_;
        std::ranges::sort(nusxa);
        return nusxa[nusxa.size() / 2];
    }

    int eng() const
    {
        if (davomiyliklar_.empty()) return 0;
        return *std::ranges::max_element(davomiyliklar_);
    }

    const std::map<Daraja, int>& darajalar() const { return daraja_; }
    const std::map<std::string, int>& manbalar() const { return manba_; }
};

int main()
{
    std::vector<Qator> jurnal = {
        {"2024-09-01", Daraja::Nosozlik,      "auth", "kirish",     42},
        {"2024-09-01", Daraja::Nosozlik,      "auth", "chiqish",    18},
        {"2024-09-01", Daraja::Xato,          "db",   "ulanish",  1500},
        {"2024-09-01", Daraja::Ogohlantirish, "db",   "sekin",     900},
        {"2024-09-01", Daraja::Halokat,       "db",   "to'xtadi", 3000},
        {"2024-09-01", Daraja::Xato,          "api",  "404",        15},
        {"2024-09-01", Daraja::Nosozlik,      "api",  "so'rov",     60},
    };

    Statistika s;
    for (const auto& q : jurnal) s.qosh(q);

    std::println("Jami qatorlar: {}", s.soni());
    std::println("");

    std::println("Darajalar bo'yicha:");
    for (const auto& [d, n] : s.darajalar()) {
        std::string ustun(static_cast<std::size_t>(n) * 3, '#');
        std::println("  {:<14} {:>2}  {}", darajaMatni(d), n, ustun);
    }

    std::println("");
    std::println("Manbalar bo'yicha:");
    for (const auto& [m, n] : s.manbalar()) {
        std::println("  {:<6} {:>2}", m, n);
    }

    std::println("");
    std::println("Davomiylik:");
    std::println("  o'rtacha: {:.2f} ms", s.ortacha());
    std::println("  median:   {} ms", s.median());
    std::println("  eng uzun: {} ms", s.eng());

    return 0;
}
Natija
Jami qatorlar: 7

Darajalar bo'yicha:
  NOSOZLIK        3  #########
  OGOHLANTIRISH   1  ###
  XATO            2  ######
  HALOKAT         1  ###

Manbalar bo'yicha:
  api     2
  auth    2
  db      3

Davomiylik:
  o'rtacha: 790.71 ms
  median:   60 ms
  eng uzun: 3000 ms
O'rtacha va median - qaysi biri to'g'ri

Chiqishga qarang: o'rtacha va median orasida katta farq bor.

Sabab - chetdagi qiymatlar. Bitta 3000 ms lik yozuv o'rtachani yuqoriga tortadi, median esa o'zgarmaydi.

Ishlash statistikasida odatda foizli qiymatlar ishlatiladi:

Ko'rsatkichNima aytadi
O'rtachaChetdagilarga sezgir - chalg'ituvchi
Median (p50)Odatiy foydalanuvchi nima ko'radi
p95Sekin 5% qanchalik sekin
p99Eng yomon holatlar

"O'rtacha javob vaqti 200 ms" degan hisobot ko'pincha yashiradi: foydalanuvchilarning 5% i 3 soniya kutgan bo'lishi mumkin.

Median hisoblashda diqqat: bizning median() juft sonli to'plamda ikki o'rta qiymatning kattarog'ini oladi. Qat'iy ta'rif bo'yicha ularning o'rtachasini olish kerak - lekin ishlash ko'rsatkichlari uchun bu farq ahamiyatsiz.

5-qadam: to'liq dastur #

C++
namespace fs = std::filesystem;

enum class Daraja { Nosozlik, Ogohlantirish, Xato, Halokat };

std::string darajaMatni(Daraja d)
{
    switch (d) {
        case Daraja::Nosozlik:      return "NOSOZLIK";
        case Daraja::Ogohlantirish: return "OGOHLANTIRISH";
        case Daraja::Xato:          return "XATO";
        case Daraja::Halokat:       return "HALOKAT";
    }
    return "?";
}

std::optional<Daraja> darajaOqi(std::string_view s)
{
    if (s == "INFO")  return Daraja::Nosozlik;
    if (s == "WARN")  return Daraja::Ogohlantirish;
    if (s == "ERROR") return Daraja::Xato;
    if (s == "FATAL") return Daraja::Halokat;
    return std::nullopt;
}

struct Qator
{
    std::string sana;
    Daraja daraja{};
    std::string manba;
    std::string xabar;
    int davomiylik{};
};

enum class QatorXatosi { BoshQator, KamMaydon, NomalumDaraja, YaroqsizVaqt };

std::vector<std::string_view> bolaklar(std::string_view s, char ajr, int soni)
{
    std::vector<std::string_view> n;
    while (soni > 1) {
        auto joy = s.find(ajr);
        if (joy == std::string_view::npos) break;
        n.push_back(s.substr(0, joy));
        s.remove_prefix(joy + 1);
        --soni;
    }
    n.push_back(s);
    return n;
}

std::expected<Qator, QatorXatosi> ajrat(std::string_view qator)
{
    if (qator.empty()) return std::unexpected{QatorXatosi::BoshQator};
    auto b = bolaklar(qator, '|', 5);
    if (b.size() < 5) return std::unexpected{QatorXatosi::KamMaydon};
    auto d = darajaOqi(b[1]);
    if (!d) return std::unexpected{QatorXatosi::NomalumDaraja};
    int ms{};
    auto [oxir, xato] = std::from_chars(b[4].data(), b[4].data() + b[4].size(), ms);
    if (xato != std::errc{}) return std::unexpected{QatorXatosi::YaroqsizVaqt};
    return Qator{std::string{b[0]}, *d, std::string{b[2]}, std::string{b[3]}, ms};
}

// Butun jarayonni boshqaradi - RAII, hech qanday qo'lda tozalash yo'q
class Tahlilchi
{
    std::vector<Qator> qatorlar_;
    int buzuq_{};

public:
    void faylniOqi(const fs::path& yol)
    {
        std::ifstream fayl{yol};
        if (!fayl) throw std::runtime_error{"fayl ochilmadi: " + yol.string()};

        std::string qator;
        while (std::getline(fayl, qator)) {
            if (qator.empty() || qator.starts_with('#')) continue;
            auto n = ajrat(qator);
            if (n) qatorlar_.push_back(std::move(*n));
            else ++buzuq_;
        }
        // fayl bu yerda avtomatik yopiladi
    }

    std::size_t soni() const { return qatorlar_.size(); }
    int buzuq() const { return buzuq_; }

    // Konsept bilan cheklangan shablon (15-bo'lim)
    template <std::predicate<const Qator&> F>
    std::vector<Qator> tanla(F shart) const
    {
        std::vector<Qator> n;
        std::ranges::copy_if(qatorlar_, std::back_inserter(n), shart);
        return n;
    }

    std::map<std::string, int> manbaHisobi() const
    {
        std::map<std::string, int> n;
        for (const auto& q : qatorlar_) ++n[q.manba];
        return n;
    }

    std::vector<Qator> engSekinlar(std::size_t nechta) const
    {
        auto nusxa = qatorlar_;
        std::ranges::partial_sort(nusxa,
            nusxa.begin() + static_cast<std::ptrdiff_t>(
                std::min(nechta, nusxa.size())),
            [](const Qator& a, const Qator& b) {
                return a.davomiylik > b.davomiylik;
            });
        nusxa.resize(std::min(nechta, nusxa.size()));
        return nusxa;
    }
};

int main()
{
    // Sinov faylini yaratamiz
    fs::path yol{"/tmp/sr_cpp_jurnal.log"};
    {
        std::ofstream f{yol};
        f << "# server jurnali\n";
        f << "2024-09-01|INFO|auth|foydalanuvchi kirdi|42\n";
        f << "2024-09-01|INFO|auth|sessiya yangilandi|18\n";
        f << "2024-09-01|ERROR|db|ulanish uzildi|1500\n";
        f << "2024-09-01|WARN|db|so'rov sekin|900\n";
        f << "2024-09-01|FATAL|db|xizmat to'xtadi|3000\n";
        f << "2024-09-01|ERROR|api|404 topilmadi|15\n";
        f << "2024-09-01|INFO|api|so'rov bajarildi|60\n";
        f << "2024-09-01|WARN|cache|kesh to'ldi|220\n";
        f << "buzuq qator\n";
        f << "2024-09-01|TRACE|api|nomalum daraja|10\n";
        f << "\n";
    }

    Tahlilchi t;
    t.faylniOqi(yol);

    std::println("JURNAL HISOBOTI");
    std::println("{}", std::string(52, '='));
    std::println("Fayl:            {}", yol.filename().string());
    std::println("O'qilgan:        {} qator", t.soni());
    std::println("Buzuq:           {} qator", t.buzuq());

    std::println("");
    std::println("Manbalar:");
    for (const auto& [m, n] : t.manbaHisobi()) {
        std::println("  {:<8} {:>2} {}", m, n, std::string(
            static_cast<std::size_t>(n) * 2, '#'));
    }

    std::println("");
    auto jiddiy = t.tanla([](const Qator& q) {
        return q.daraja >= Daraja::Xato;
    });
    std::println("Jiddiy hodisalar ({} ta):", jiddiy.size());
    for (const auto& q : jiddiy) {
        std::println("  {:<8} {:<6} {}", darajaMatni(q.daraja), q.manba, q.xabar);
    }

    std::println("");
    std::println("Eng sekin uchta:");
    for (const auto& q : t.engSekinlar(3)) {
        std::println("  {:>5} ms  {:<6} {}", q.davomiylik, q.manba, q.xabar);
    }

    std::println("");
    auto sekinDb = t.tanla([](const Qator& q) {
        return q.manba == "db" && q.davomiylik > 1000;
    });
    std::println("db manbasidagi 1000 ms dan sekin: {} ta", sekinDb.size());

    fs::remove(yol);
    std::println("");
    std::println("vaqtinchalik fayl tozalandi: {}", !fs::exists(yol));

    return 0;
}
Natija
JURNAL HISOBOTI
====================================================
Fayl:            sr_cpp_jurnal.log
O'qilgan:        8 qator
Buzuq:           2 qator

Manbalar:
  api       2 ####
  auth      2 ####
  cache     1 ##
  db        3 ######

Jiddiy hodisalar (3 ta):
  XATO     db     ulanish uzildi
  HALOKAT  db     xizmat to'xtadi
  XATO     api    404 topilmadi

Eng sekin uchta:
   3000 ms  db     xizmat to'xtadi
   1500 ms  db     ulanish uzildi
    900 ms  db     so'rov sekin

db manbasidagi 1000 ms dan sekin: 2 ta

vaqtinchalik fayl tozalandi: true
Nima uchun Tahlilchi da destruktor yo'q

Sinf 200 qatordan iborat, fayl ochadi, vektor va lug'atlar boshqaradi - lekin destruktori yo'q.

Sabab - 12-bo'limdagi nol qoidasi:

A'zoKim tozalaydi
std::vector<Qator>O'zi
std::ifstreamO'zi (qamrovdan chiqqanda)
std::stringO'zi
std::mapO'zi

Har bir a'zo o'z resursini biladi. Tahlilchi esa faqat ularni birlashtiradi.

Natijada:

  • Nusxalanadi (chuqur nusxa, avtomatik)
  • Ko'chiriladi (arzon, avtomatik)
  • Istisno xavfsiz (avtomatik)
  • Sizib chiqmaydi (avtomatik)

Va bularning birortasi uchun kod yozilmagan.

Bu zamonaviy C++ ning asosiy g'oyasi: resursni boshqarish sinflarni kichik va maxsus qiling, qolgan hamma narsa ularni faqat ishlatsin.

Bundan keyin nima #

C++
int main()
{
    std::println("QAYERGA DAVOM ETISH");
    std::println("{}", std::string(58, '='));
    std::println("");
    std::println("{:<26} {}", "MAVZU", "NIMA UCHUN");
    std::println("{}", std::string(58, '-'));
    std::println("{:<26} {}", "Modullar (C++20)", "Sarlavhalarsiz yig'ish");
    std::println("{:<26} {}", "Korutinlar (C++20)", "Asinxron kod");
    std::println("{:<26} {}", "Sinov ramkalari", "Catch2, doctest");
    std::println("{:<26} {}", "Yig'ish tizimi", "CMake yoki Meson");
    std::println("{:<26} {}", "Paket menejeri", "vcpkg yoki Conan");
    std::println("{:<26} {}", "Profillash", "perf, valgrind");
    std::println("{:<26} {}", "Statik tahlil", "clang-tidy");
    std::println("{:<26} {}", "Formatlash", "clang-format");

    std::println("");
    std::println("Amaliyot g'oyalari:");
    std::println("  1. Ushbu tahlilchiga JSON chiqishini qo'shing");
    std::println("  2. Katta fayl uchun ko'p oqimli o'qish (18-bo'lim)");
    std::println("  3. Filtrlarni buyruq qatoridan o'qing");
    std::println("  4. Sana bo'yicha guruhlash qo'shing (17-bo'lim)");
    std::println("  5. Har bir qismga birlik sinovlari yozing");

    std::println("");
    std::println("Eng muhim maslahat:");
    std::println("  Har kuni oz-ozdan yozing.");
    std::println("  Har bir dasturni sanitayzer bilan ishga tushiring.");
    std::println("  Kompilyator ogohlantirishlarini o'qing.");

    return 0;
}
Natija
QAYERGA DAVOM ETISH
==========================================================

MAVZU                      NIMA UCHUN
----------------------------------------------------------
Modullar (C++20)           Sarlavhalarsiz yig'ish
Korutinlar (C++20)         Asinxron kod
Sinov ramkalari            Catch2, doctest
Yig'ish tizimi             CMake yoki Meson
Paket menejeri             vcpkg yoki Conan
Profillash                 perf, valgrind
Statik tahlil              clang-tidy
Formatlash                 clang-format

Amaliyot g'oyalari:
  1. Ushbu tahlilchiga JSON chiqishini qo'shing
  2. Katta fayl uchun ko'p oqimli o'qish (18-bo'lim)
  3. Filtrlarni buyruq qatoridan o'qing
  4. Sana bo'yicha guruhlash qo'shing (17-bo'lim)
  5. Har bir qismga birlik sinovlari yozing

Eng muhim maslahat:
  Har kuni oz-ozdan yozing.
  Har bir dasturni sanitayzer bilan ishga tushiring.
  Kompilyator ogohlantirishlarini o'qing.
Yakuniy topshiriq
  1. Tahlilchiga --filtr buyruq qatori parametrini qo'shing.
  2. Chiqishni JSON formatida ham bera oladigan qiling.
  3. Qator uchun std::formatter ixtisoslashtiring (13-bo'lim).
  4. Buzuq qatorlar ro'yxatini sabab bilan birga saqlang.
  5. p95 foizli ko'rsatkichni hisoblang.
  6. Bir necha faylni birga tahlil qiling.
  7. Katta fayl uchun std::async bilan parallel o'qing (18-bo'lim).
  8. Sana bo'yicha guruhlab kunlik hisobot chiqaring (17-bo'lim).
  9. Dasturni -fsanitize=address,undefined bilan yig'ib sinang (19-bo'lim).
  10. -Wall -Wextra -Wpedantic -Werror bilan yig'ing va barcha ogohlantirishlarni tuzating.

Xulosa #

  • Haqiqiy dastur - alohida imkoniyatlar to'plami emas, ularning birgalikda ishlashi.
  • Ma'lumot turi struct bo'lsin - invariant bo'lmasa nol qoidasi yetarli.
  • Ajratishda string_view ishlating, saqlashda string ga nusxa oling.
  • Xato sababi kerak bo'lsa - std::expected, kerak bo'lmasa optional.
  • Ish paytida tanlanadigan xatti-harakat - virtual interfeys + unique_ptr.
  • Kompozit naqsh filtrlarni birlashtirish imkonini beradi.
  • Statistikada o'rtacha chalg'itadi - median va foizli ko'rsatkichlarni bering.
  • Yuqori darajadagi sinf destruktor yozmasin - a'zolar o'zlarini tozalasin.
  • Butun loyihada birorta new yoki delete bo'lmasligi mumkin - va bo'lmasligi kerak.
  • Har bir yig'ishda -Wall -Wextra -Wpedantic, sinovlarda esa sanitayzerlar.

Darslik shu bilan yakunlandi. Endi eng muhim qadam sizniki: o'z loyihangizni yozing. C++ ni o'qib emas, yozib o'rganiladi.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.